Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to build a lazy conditional metafunction

Suppose I want to use std::conditional to determine a type, if the type is a vector<...> the return will be a vector<...>::size_type and if not it will be int. (just an example).

A naive way to use std::conditional:

template<class V> struct is_vector : std::false_type{};
template<class T> struct is_vector<std::vector<T>> : std::true_type{};

template<class C>
using my_size_type = typename std::conditional<
    not is_vector<C>::value, 
    int, 
    C::size_type // note that this line only makes sense when condition is false
>::type;

However this fails because if C is say a double, double::size_type will give an error, even if the that is the evaluation of the second false option.

So, I am wonder if there is a sort of lazy_conditional in which the false (or the second false) statement is not evaluated.

I found something here: https://stackoverflow.com/a/5317659/225186 but I don't know how to use it my example.


Note that I know how to get the same result without using std::conditional:

template<class V> struct my_size_type{typedef int type;};
template<class T> struct my_size_type<std::vector<T>>{typedef std::vector<T>::size_type type;};

The question is if there is a lazy_conditional that somehow encapsulated a std::conditional that is short circuited.


After some trial error I manage to use the ideas in https://stackoverflow.com/a/5317659/225186 and get to this that follows. It also makes me think that it is not possible to write std::lazy_conditional because C::size_type cannot appear at all in any expression a priori, so two-step expressions are needed.

template<class C, bool B> struct false_case{
    typedef void type;
};
template<class C> struct false_case<C, false>{
    typedef typename C::size_type type;
};

template<class C>
using size_type = typename std::conditional<
    not is_vector<C>::value, 
    int, 
    typename false_case<C, not is_vector<C>::value>::type
>::type;

I couldn't even condense this into a macro, because each case is different.

like image 611
alfC Avatar asked Dec 15 '15 04:12

alfC


1 Answers

You need a level of indirection.

template<class T> struct identity { using type = T; };

template<class C> 
struct size_type_of : identity<typename C::size_type> { };

template<class C>
using size_type = typename std::conditional<not is_vector<C>::value,
                                            identity<int>,
                                            size_type_of<C>>::type::type;

The point is to delay looking at C::size_type (by instantiating size_type_of<C>) until you know it has one.


If what you really want to do is "C::size_type if it exists, int otherwise", then std::experimental::detected_or_t is your friend:

template<class C>
using size_type_t = typename C::size_type;

template<class C>
using size_type_or_default = std::experimental::detected_or_t<int, size_type_t, C>;
like image 54
T.C. Avatar answered Nov 17 '22 19:11

T.C.