2010-08-17 1 views
3

Я пытаюсь предоставить пользователям класс (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() 
    } 
}; 

Я попытался следующие, но я не полностью satistified:

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

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

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

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

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

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

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

+0

Что делает «проверка»? Я понимаю проблему, но трудно дать правильный ответ, когда я не знаю, что делать с 'check' в первую очередь. (Все они возвращают 0; '? Откуда взялось это значение?) – GManNickG

+0

check() фактически опросит Blackboard для того, был ли размещен какой-либо объект на нем и доступен ли он. В полной реализации он возвращает shared_ptr к опубликованному объекту (если есть) или null shared_ptr. –

ответ

2

Вам нужен шаблон функции-члена 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 

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

+0

да, это красиво, спасибо огромное, отличный материал! –

Смежные вопросы