Как сделать шаблон с производным поведением? - PullRequest
5 голосов
/ 25 июня 2019

У меня есть шаблон Conditional, который возвращает value в соответствии с логическим выражением:

template<bool C, typename C1, typename C2>
struct Conditional {
};

template<typename C1, typename C2>
struct Conditional<true, C1, C2> {
    typedef C1 value;
};

template<typename C1, typename C2>
struct Conditional<false, C1, C2> {
    typedef C2 value;
};
<Conditional<(0 != 1), Int<0>, Int<1>>::value; // Int<0>
<Conditional<(0 == 1), Int<0>, Int<1>>::value, // Int<1>

Я хочу создать шаблон ConditionalInteger, в котором его поведение основано на Condition

ConditionalInteger<(0 != 1), 0, 1>::value == 0; // true
ConditionalInteger<(0 == 1), 0, 1>::value == 1  // false

При прямолинейном подходе это работает:

template<bool C, int N1, int N2>
struct ConditionalInteger {
};

template<int N1, int N2>
struct ConditionalInteger<true,N1,N2> {
    static constexpr int value = N1;
};

template<int N1, int N2>
struct ConditionalInteger<false,N1,N2> {
    static constexpr int value = N2;
};

Как мне реализовать это, используя Conditional?

При следующей попытке я получаю сообщение об ошибке:

Нет типа с именем "value" в "struct IntWrapper <0>"

template<int N>
struct IntWrapper {
    static constexpr int value = N;
};

template<bool C, int N1, int N2>
struct ConditionalInteger {
    using value = typename Conditional<C, typename IntWrapper<N1>::value, typename IntWrapper<N2>::value>::value;
};

Ответы [ 2 ]

6 голосов
/ 25 июня 2019

Я думаю, это то, что вы хотите:

template<bool C, int N1, int N2>
struct ConditionalInteger {
    static constexpr int value = 
        Conditional<C, IntWrapper<N1>, IntWrapper<N2>>::value::value;
};

static_assert(ConditionalInteger<true, 2, 3>::value == 2);
static_assert(ConditionalInteger<false, 2, 3>::value == 3);

Conditional принимает типы, и вы можете указать два типа: IntWrapper<N1> и IntWrapper<N2>. Возвращает тип Conditional<C, IntWrapper<N1>, IntWrapper<N2>>::value, который IntWrapper<N>. Затем вы извлекаете N из этого типа еще с одним ::value. Вам не нужно typename здесь.

Вы также можете получить ConditionalInteger из IntWrapper:

template<bool C, int N1, int N2>
struct ConditionalInteger : IntWrapper<
    Conditional<C, IntWrapper<N1>, IntWrapper<N2>>::value::value> {};

Более подробная реализация:

template<bool C, int N1, int N2>
struct ConditionalInteger {
    using Int = typename Conditional<C, IntWrapper<N1>, IntWrapper<N2>>::value;
    static constexpr int value = Int::value;
};
0 голосов
/ 25 июня 2019

Очевидно, что самым простым способом было бы вообще не использовать специализацию:

template<bool C, int N1, int N2>
struct ConditionalInteger {
    static constexpr int value = C ? N1 : N2;
};

Тернарный оператор можно использовать в контексте constexpr.

Но тогда я бы использовал что-то вродеthis:

template<auto c>
using constant = std::intergral_constant<decltype(c), c>;

using my_constant = constant<condition ? 4 : 8>;

Нет необходимости определять новый вызов, просто добавьте несколько псевдонимов.

...