How to make a template with derived behavior?
Asked Answered
C

2

5

I have a template Conditional which returns value according to the boolean expression:

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>

I want to make a ConditionalInteger template where his behavior derives from Condition

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

With straight forward approach it works:

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;
};

How do I implement it using Conditional?

With the next try, I get an error:

No type named ‘value’ in ‘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;
};
Crashland answered 25/6, 2019 at 10:29 Comment(0)
U
6

I guess this is what you want:

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 accepts types, and you give it two types: IntWrapper<N1> and IntWrapper<N2>. It returns a type Conditional<C, IntWrapper<N1>, IntWrapper<N2>>::value, which is IntWrapper<N>. Then you extract N from this type with one more ::value. You don't need typenames here.

You can also derive ConditionalInteger from IntWrapper:

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

More verbose implementation:

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;
};
Uraemia answered 25/6, 2019 at 10:35 Comment(0)
S
0

Obviously, the simplest way would be to not use specialization at all:

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

The ternary operator can be used in constexpr context.

But then I'd use something like this:

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

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

No need to define a new call, simply add some aliases.

Staw answered 25/6, 2019 at 15:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.