Does there exist a lazy equivalent of std::conditional in C++?

Viewed 239

Upon calling std::contional<bool, T1, T2> both the types T1 and T2 get instantiated as soon as its called, irrespective of the value of the bool, does there exist an implementation (or some pointers to implement it) where only the relevant type is instantiated.

Following code for example is impossible to write using std::conditional. We can't even use SFINAE over structs (get in the example).

struct Term {};

template <int N, typename T, typename ...Ts>
struct PairList
{
    static constexpr int i = N;
    using type = T;
    using tail = PairList<N + 1, Ts...>;
};

template <int N, typename T>
struct PairList<N, T>
{
    static constexpr int i = N;
    using type = T;
    using tail = Term;
};


template <int N, typename pairList>
struct get
{
    static constexpr auto good = (N == pairList::i);
    using T =  typename std::conditional<
                   good,
                   typename pairList::type,
                   typename get<N, typename pairList::tail>::T>::type;  // The second parameter might not be defined
};
2 Answers

You can use if constexpr to lazily instantiate templates while keeping familiar code structure:

// C++20: std::type_identity
template<typename T>
struct type_t {
    using type = T;
};

template <int N, typename pairList>
struct get
{
    static auto choose_type() {
        if constexpr (N == pairList::i) {
            return type_t<typename pairList::type>{};
        } else {
            return type_t<typename get<N, typename pairList::tail>::T>{};
        }
    }

    using T = typename decltype(choose_type())::type;
};

I usually use template specialization to avoid this sort of problem

// bool is false -> recursion
template <int N, typename pairList, bool = N == pairList::i>
struct get : public get<N, typename pairList::tail>
 { };

// bool is true -> stop recursion and type is selected
template <int N, typename pairList>
struct get<N, pairList, true>
 { using T = typename pairList::type; };

The following is a full compiling example

#include <type_traits>

struct Term {};

template <int N, typename T, typename ...Ts>
struct PairList
{
    static constexpr int i = N;
    using type = T;
    using tail = PairList<N + 1, Ts...>;
};

template <int N, typename T>
struct PairList<N, T>
{
    static constexpr int i = N;
    using type = T;
    using tail = Term;
};

// bool is false -> recursion
template <int N, typename pairList, bool = N == pairList::i>
struct get : public get<N, typename pairList::tail>
 { };

// bool is true -> stop recursion and type is selected
template <int N, typename pairList>
struct get<N, pairList, true>
 { using T = typename pairList::type; };


int main ()
 {
   using T0 = PairList<0u, int, long, long long>;
   using T1 = get<1u, T0>::T;

   static_assert( std::is_same_v<T1, long>, "!" );
 }
Related