How to implement If-Else Conditional template?
Asked Answered
S

2

5

I have Conditional template

template<bool C, typename ...>
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;
};

Which seems to work as expected:

<Conditional<(0 != 1), Int<0>, Int<1>>::value; // Int<0>
<Conditional<(0 == 1), Int<0>, Int<1>>::value, // Int<1>

But it evaluates all values before returning:

template<typename G, typename M>
struct DoMove {
private:
    constexpr static bool _isRLMove = (M::direction == Direction::LEFT || M::direction == Direction::RIGHT);
public:
    using result = typename Conditional<
            _isRLMove, typename DoMoveRL<G, M>::result, typename DoMoveUD<G, M>::result>::value;
                       // ^ when _isRLMove == false evaluates this
};

Which causes compile errors from DoMoveRL.

How can one evaluate according to condition? What am I missing here?

Salinger answered 27/6, 2019 at 8:49 Comment(1)
Notice than standard provides std::conditional (since C++11)Widdershins
W
6

DoMoveRL<G, M>::result forces instantiation of DoMoveRL<G, M>.

You might delay the retrieval of result to avoid to force instantiation:

template<typename G, typename M>
struct DoMove {
private:
    constexpr static bool _isRLMove = (M::direction == Direction::LEFT
                                    || M::direction == Direction::RIGHT);
public:
    using result = typename Conditional<
            _isRLMove, DoMoveRL<G, M>, DoMoveUD<G, M>>::value::result;
};
Widdershins answered 27/6, 2019 at 9:11 Comment(1)
That's just perfect... Thanks!Salinger
H
1

There are a couple ways you could go about this. Traditionally, add a layer of indirection (live example):

template<bool C, template<typename ...> class...>
struct Conditional {
};


template<template<typename...> class C1, template<typename...> class C2>
struct Conditional<true, C1, C2> {
    template<typename... Ts>
    using apply = C1<Ts...>;
};


template<template<typename...> class C1, template<typename...> class C2>
struct Conditional<false, C1, C2> {
    template<typename... Ts>
    using apply = C2<Ts...>;
};

using result = typename Conditional<_isRLMove, DoMoveRL, DoMoveUD>::template apply<G, M>::result;

You can simplify this a bit if instantiating the templates themselves is fine and only accessing result breaks. I've accounted for the templates themselves breaking.

In C++17, you can use if constexpr if you like that better (live example):

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

static auto choose_type() {
    if constexpr (_isRLMove) {
        return type_val<typename DoMoveRL<G, M>::result>{};
    } else {
        return type_val<typename DoMoveUD<G, M>::result>{};
    }
}

using result = typename decltype(choose_type())::type;
Hautevienne answered 27/6, 2019 at 9:13 Comment(1)
Thanks, it's a great answerSalinger

© 2022 - 2024 — McMap. All rights reserved.