Устранение неоднозначности вызовов функций без аргументов в вариативных иерархиях классов

Я пытаюсь предоставить пользователям класса (MyGizmo ниже), который является производным от вариативной иерархии (ObjGetter ниже), с простым, лаконичным способом однозначного вызова функции-члена, которая не принимает аргументов (check() ниже). Я могу заставить это работать с функциями, которые принимают аргументы (например, функцию tune() ниже), но я не нашел способа заставить это работать для функций, которые не принимают аргументов.

struct Base { };
struct ObjA : public Base { };
struct ObjB : public Base { };
struct ObjC : public Base { };

template <class ... Obj> struct ObjGetter;

template <class Obj, class ... Tail>
struct ObjGetter<Obj, Tail ...> : public ObjGetter<Tail ...>
{
  using ObjGetter<Tail ...>::tune;  // resolve ambiguous lookups for tune()

  void tune(Obj * obj) { } // no problem with this one, disambiguated by obj type

  Obj * check() const { return 0; } // problem with this one, no arg to disambiguate
};

template <> struct ObjGetter<> { // to terminate the recursion
  void tune(void);  // needed by the using statement above but should not be used, hence different syntax
};

struct MyGizmo : public ObjGetter<ObjA, ObjC> // variadic
{
  void testit() {
    ObjA * a = 0; ObjB *b = 0; ObjC *c = 0;

    a = ObjGetter<ObjA, ObjC>::check(); // too ugly!
    c = ObjGetter<ObjC>::check(); // too ugly!

    tune(a); // no problem
    //tune(b); // correct compile-time error: no matching function for call to ‘MyGizmo::tune(ObjB*&)’
    tune(c); // no problem

    // I would like a simple syntax like this:
    //a = check<ObjA>(); // should call ObjGetter<ObjA, ObjC>::check()
    //b = check<ObjB>(); // should give a compile-time error
    //c = check<ObjC>(); // should call ObjGetter<ObjC>::check()
  }
};

Я пробовал следующее, но не полностью удовлетворен:

Во-первых, я могу использовать вторичный класс с простым шаблоном, который перемещается по иерархии, чтобы уменьшить уродливый вызов, чтобы иметь только один аргумент шаблона; дает что-то вроде:

a = ObjGetterHelper<ObjA>::check(); // still ugly! MyGizmo user should not have to know about ObjGetterCore
c = ObjGetterHelper<ObjC>::check(); // too ugly!

Я могу использовать помощник Type2Type и передать check() аргумент, это работает нормально, выглядит так:

a = check(Type2Type<ObjA>()); // pretty ugly too
c = check(Type2Type<ObjC>()); // pretty ugly too

Я мог бы использовать макросы, но я не хочу туда идти...

#define CHECK(X) check(Type2Type<X>())

Я думаю, что псевдонимы шаблонов обеспечат решение, но я использую g++, который еще не поддерживает их. Есть ли что-нибудь еще в то же время? Большое спасибо!


person Laurent Itti    schedule 17.08.2010    source источник
comment
Что делает check? Я понимаю проблему, но трудно дать правильный ответ, когда я вообще не знаю, что делать с check. (Все они return 0;? Откуда берется значение?)   -  person GManNickG    schedule 18.08.2010
comment
check() на самом деле опрашивает Blackboard на предмет того, был ли размещен на нем какой-либо объект и доступен ли он. В полной реализации он возвращает shared_ptr опубликованному объекту (если он присутствует) или null shared_ptr.   -  person Laurent Itti    schedule 18.08.2010


Ответы (1)


Вам нужен шаблон функции-члена check<Type> с некоторой структурой для делегирования вверх по цепочке наследования, если тип не соответствует заголовку вариационного списка.

Это классическая задача для SFINAE.

  template< class Obj2 >
  typename std::enable_if< std::is_same< Obj, Obj2 >::value, Obj * >::type
  check() const { return 0; } // perform work

  template< class Obj2 >
  typename std::enable_if< ! std::is_same< Obj, Obj2 >::value, Obj2 * >::type
  check() const { return base::template check< Obj2 >(); } // delegate

Работает так же, как мой другой ответ. Я оставлю это как пример барочной глупости.

person Potatoswatter    schedule 17.08.2010