2013-07-01 2 views
0
template<typename TList/*TList - Alexandrescu's typelist*/> 
class TheClass 
{ 
    void foo_public (const TypeAt<TList, 0>& t0, const TypeAt<TList, 1>&t1) 
    { 
    foo_private(t0, t1) 
    } 
    void foo_private (const TypeAt<TList, 0>& t0, const TypeAt<TList, 1>&t1) 
    { 
    /*do stuff with t1*/ 
    foo_private(t0); 
    } 

    void foo_public (const TypeAt<TList, 0>& t0) 
    { 
    foo_private(t0); 
    } 
    void foo_private(const TypeAt<TList, 0>& t0) 
    { 
    /*do stuff with t0*/ 
    } 
}; 

int main() 
{ 
    typedef MakeList<int, int> TheList; //variant 1 - works fine 
    TheClass<TheList> c; 

    typedef MakeList<int> TheList; //variant 2 - compile error 
    TheClass<TheList> c; 
} 

Очевидно, это происходит потому, что в случае # 2, потому что компилятор не может скомпилировать foo_public/private метод, удерживающих два аргумента, поскольку шаблон TypeAt<TList, 1> не является действительным для списка, содержащего один типа.TypeList, несоответствие количества параметров шаблона

Как я могу решить эту проблему общим способом, не специализируясь на TheClass для списков типов 1,2,3 ...? Может быть, что-то вроде «SFINAE-way»?

+0

Что вы пытаетесь достичь? – Nawaz

+0

@Nawaz: Я хочу создать сериализатор шаблонов. Присвойте шаблон шаблону, например 'serializator >', а затем 'serialize (10,12, reference_to_some_class_object)' – fogbit

ответ

0

ПРИМЕЧАНИЕ: Я полагал, что специализация шаблонов не является решением, потому что класс намного больше, чем в коде, который вы нам предоставляете (поскольку вы сосредоточили свой вопрос на проблеме, молодцы). Все здесь ненавидят вопросы с тысячами строк кода и вопрос вроде: «Что случилось?»)

У вас есть доступ к C++ 11? Если ответ да, то не использовать списки типов лков стиля, использовать более простые списки типов VARIADIC-шаблоны на основе:

template<typename... Ts> 
struct TypeList{}; 

Реализации TypeList операции слишком просто.

Даже если вы используете списки типов локи стиле или VARIADIC списки типов шаблонов, решение для вашей проблемы точно так же: enable_if

template<bool CONDITION , typename T> 
struct enable_if; 

template<typename T> 
struct enable_if<true,T> { typedef type T; }; 

template<typename T> 
struct enable_if<false,T> {}; 

Как вы, вероятно, заметили, ложный specialitation не определен ЬурейеЕ type из тип T. Это означает, что если вы используете enable_if в двух местах, где условие истинно и где условие ложно, будет создано только одно из мест. Зачем? Потому что вы используете член type в обоих, но в фальшивом случае его не определено.

Смотрите пример: (Supose мы имеем metafunction is_floating_point, которая проверяет, является ли данный тип является типом с плавающей точкой)

template<typename T> 
typename enable_if<is_floating_point<T>::value , bool> are_equal(const T& a , const T& b) 
{ 
    const T epsilon = 0.0001; 

    return std::abs(a-b) < epsilon; 
} 

template<typename T> 
typename enable_if<!is_floating_point<T>::value , bool> are_equal(const T& a , const T& b) 
{ 
    return a == b; 
} 

В этом примере enable_if используется условно генерировать версию с плавающей точкой функция сравнения.

Но в вашем случае есть проблема. Правильная реализация функции шаблона может зависеть только от собственных аргументов, как в примере (зависит от T, который является параметром функции).
Но это не ваш случай: представление одного параметра перегрузки foo_private и foo_public не зависит от функциональных параметров, зависит от классового требования (список типов).

В C++ 11 вы можете решить эту проблему написания функции как функции шаблона, и проходящее состояние не-функции (размер TypeList в нашем случае) в качестве значения по умолчанию шаблона:

template<unsigned int size_of_list = SizeOf<TypeList>::value> 
    typename enable_if<size_of_list > 1 , void>::type foo_public (const TypeAt<TList, 0>& t0, const TypeAt<TList, 1>&t1) 
    { 
    foo_private(t0, t1) 
    } 
    template<unsigned int size_of_list = SizeOf<TypeList>::value> 
    typename enable_if<size_of_list > 1 , void>::type foo_private (const TypeAt<TList, 0>& t0, const TypeAt<TList, 1>&t1) 
    { 
    /*do stuff with t1*/ 
    foo_private(t0); 
    } 

Если у вас нет доступа к чему-либо из C++ 11, вы можете попробовать другой способ.Предположим, у нас есть условный селектор типа:

template<bool CONDITION , typename T , typename U> 
struct conditional; 

template<typename T , typename U> 
struct conditional<true,T,U> { typedef type T; }; 

template<typename T , typename U> 
struct conditional<false,T,U> { typedef type U; }; 

Другой способ решения ваших перегруженных является использование unmeanning типа (Innaccesible пользователем) в том случае, когда TypeList содержит только один тип:

template<typename TList/*TList - Alexandrescu's typelist*/> 
class TheClass 
{ 

    class UnmeanningType {}; //This type must be private 

    typedef typename conditional<SizeOf<TList>::value > 1 , TypeAt<TList , 1> , UnmeanningType>::type SecondArgType; 

    void foo_public (const TypeAt<TList, 0>& t0, const SecondArgType& t1) 
    { 
    foo_private(t0, t1) 
    } 
    void foo_private (const TypeAt<TList, 0>& t0, const SecondArgType& t1) 
    { 
    /*do stuff with t1*/ 
    foo_private(t0); 
    } 

    void foo_public (const TypeAt<TList, 0>& t0) 
    { 
    foo_private(t0); 
    } 
    void foo_private(const TypeAt<TList, 0>& t0) 
    { 
    /*do stuff with t0*/ 
    } 
}; 

При таком подходе, если список типов содержит только один тип, перегрузка двух аргументов использует UnMeanningType в качестве типа seccond arg, который пользователь (теоретически) не знает. Таким образом, теоретически, пользователь может использовать только одну переменную arg в этом случае.

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

  • Нет связанных вопросов^_^