2015-09-25 2 views
2

это то, что я хочу, а «переключатель» тип признака, который возвращает первый тип, который имеет состояние == TRUE:реализации выключателю типа признака (с станд :: conditional_t ​​цепи вызовов)

ext::select_t<condition1 == true, Type1, 
       condition2 == true, type2, 
       condition3 == true, type3> 

и т.д. , и иметь возможность добавить столько пар параметров/типов, сколько захочу.

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

template<typename Number, 
     typename Distribution = std::conditional_t< 
       // IF 
       std::is_integral<Number>::value, 
       // RETURN INT 
       std::uniform_int_distribution<Number>, 
       // ELSE 
       std::conditional_t<std::is_floating_point<Number>::value, 
            // RETURN REAL 
            std::uniform_real_distribution<Number>, void>>> 

Number random(Number min, Number max) 
{ 
    static std::random_device rd; 
    static std::mt19937 mt(rd()); 

    Distribution dist(min, max); 

    return dist(mt); 
} 

, как вы можете видеть, что это решает во время компиляции, что тип распределения я хочу в зависимости от условий/типов прошли.

Очевидно, это может стать настоящим уродливым, если я попытаюсь добавить дополнительные условия, представьте, что я хочу 10 из них.

так я пытался построить один, но с треском провалился:

template<bool B, typename T> 
struct cond 
{ 
    static constexpr bool value = B; 
    using type = T; 
}; 

template<typename Head, typename... Tail> 
struct select 
{ 
    using type = std::conditional_t<Head::value, typename Head::type, select<Tail...>>; 
}; 

template<typename Head> 
struct select<Head> 
{ 
    using type = std::conditional_t<Head::value, typename Head::type, void>; 
}; 

template<typename Head, typename... Tail> 
using select_t = typename select<Head, Tail...>::type; 

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

using Type = std::select_t<cond<false, void>, 
          cond<false, int>, 
          cond<true, std::string>>; 

не только это не выглядит так хорошо, как я хочу, окончательный вариант будет, но это не делает даже Работа! он работает только при первом условии.

Есть ли что-нибудь, что не хватает? также как я могу достичь этого более чистым способом (по крайней мере для конечного пользователя).

благодарит заранее.

+0

Что делать, если ни одно из условий не является истинным? – jrok

+0

, если они не верны, он просто возвращает void. очевидно, оптимальным решением было бы просто не скомпилировать, если нет истинного условия. в зависимости от того, что делает конечный пользователь, void (почти) всегда сделает компиляцию неудачной, но она может как-то скомпилироваться и сделать ошибку в коде, не знаю, как решить эту проблему тоже. – sap

ответ

4

Проблема заключается в вашем базовом случае:

using type = std::conditional_t<Head::value, typename Head::type, select<Tail...>>; 

Вы хотите на успех (Head::value) использовать тип головки (Head::type), но в случае неудачи использовать тип хвоста. Но select<Tail...> не является хвостовым. Это метафорум. Вы действительно хотите оценить его:

using type = std::conditional_t< 
       Head::value, 
       typename Head::type, 
       typename select<Tail...>::type>; 

Сейчас это немного неэффективно, так как вы должны обрабатывать полноту условном наверху. Для этого вы могли бы написать отдельный метафунд, который Boost.MPL назвал eval_if.Вместо того, чтобы логическое и два типа, он принимает логическое значение и два метафункции:

template <bool B, typename TrueF, typename FalseF> 
struct eval_if { 
    using type = typename TrueF::type; 
}; 

template <typename TrueF, typename FalseF> 
struct eval_if<false, TrueF, FalseF> { 
    using type = typename FalseF::type; 
};  

template <bool B, typename T, typename F> 
using eval_if_t = typename eval_if<B, T, F>::type; 

С которой ваш главный случай для select становится:

template<typename Head, typename... Tail> 
struct select 
{ 
    using type = eval_if_t<Head::value, 
          Head, 
          select<Tail...>>; 
}; 

Хотя на отражение, то же самое может быть достигнуто с std::conditional_t и наследование:

template <typename Head, typename... Tail> 
struct select 
: std::conditional_t<Head::value, Head, select<Tail...>> 
{ }; 

Также , Как правило, мы бы просто иметь «еще» дело в конце, так что, может быть, вы бы написать селектор, как:

using T = select_t<cond<C1, int>, 
        cond<C2, float>, 
        double>; 

Так что я хотел бы предложить писать базовый случай таким образом:

template <typename T> 
struct select<T> 
{ 
    using type = T; 
}; 

template <bool B, typename T> 
struct select<cond<B, T>> 
{ 
    // last one had better be true! 
    static_assert(B, "!"); 
    using type = T; 
}; 

Также , вы написали std::select_t ... не помещайте это в пространство имен std, поместите его в свое собственное пространство имен.

+0

еще один комментарий: else "тоже хорошая идея, я даже могу заставить ее возвратить значение по умолчанию. но окончательный вопрос: я не могу это сделать без контура struct, у которого есть пара правильно? даже используя, как 2 вариационных аргумента шаблона? Я тоже это пробовал, но не смог заставить его работать. В любом случае, спасибо много раз. – sap

+1

@sap С несколькими аргументами шаблонов все они должны быть типами. Вам нужно передать что-то вроде 'std :: true_type' вместо' true'. В противном случае реализация будет одинаковой - просто отделите два аргумента из пакета за раз, а не один. – Barry

+0

благодарит @ Barry плохо попробуйте, что позже, как упражнение, большое спасибо за помощь. – sap

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