How std::conditional works - c++

We have this little metaprogramming marvel called std::conditional described here. In the same reference it says that a possible implementation is
template<bool B, class T, class F>
struct conditional { typedef T type; };
template<class T, class F>
struct conditional<false, T, F> { typedef F type; };
So if in code I do something like
typename std::conditional<true,int,double>::type a;
the compiler will follow the first definition and if I do something like
typename std::conditional<false,int,double>::type b
the compiler will take the second. Why does that work ? What compilation rule is in place here ?

In short it has to do with template specialization rules. These are like function overloads but for types.
Here compiler prefers a more specialized type to the more general type.
template<bool B, class T, class F>
struct conditional { typedef T type; };
template<class T, class F>
struct conditional<false, T, F> { typedef F type; };
So if a template instantiation coditional<false, ...> is seen by compiler it finds:
the general template template<bool B, class T, class F> struct conditional
all its specializations: template<class T, class F> struct conditional<false, T, F>
At that point it tries to match as many specialized arguments as possible and ends up selecting the specialization with false.
For example introducing another version like:
template<class F>
struct conditional<false, int, F> { typedef int type; };
and instantiating a template type like conditional<false, int, double> will prefer specialization
template<class F> struct conditional<false, int, F>
to
template<class T, class F> struct conditional<false, T, F> which is more general compared to the version with 2 specialized paramaters.
Some tricks at this point:
It is even OK to just declare the most generic case (i.e. generic form of the template is just declared but not defined) and only have specializations for cases you really intend to have. All non-specialized cases will result in compile errors and asking the user to specialize the case for a particular type.

Why does that work ? What compilation rule is in place here ?
I'm not an expert but I'll try to explain from the pratical point of view.
Hoping to use the rights terms...
With
template <bool B, class T, class F>
struct conditional { typedef T type; };
(but, with C++11, I prefer
template <bool B, typename T, typename>
struct conditional { using type = T; };
) you declare the template
template <bool, typename, typename>
struct conditional;
and define the generic (not specialized) version.
With
template< class T, class F>
struct conditional<false, T, F> { typedef F type; };
(or, in C++11,
template <typename T, typename F>
struct conditional<false, T, F> { using type = F; };
) you define a partial specialization of conditional
When the template arguments of a class (or struct) match two or more definitions, the compliler choose the more specialized one; so, for
typename std::conditional<false,int,double>::type
both definitions of the class match so the compiler choose the specialized one (the specialized with false) anche type is double.
For
typename std::conditional<true,int,double>::type a;
only the generic version match, so type is int.

In most of the template meta programming main rule at play here is SFINAE(Substitution failure is not an error). According to this rule if compiler not find the type then instead of throwing an error it moves forward and check if there is any future statement that can provide type information.

Related

Partial Specialization using a qualified name

I'm trying to partially specialize a template for a metafunction and ran into a problem.
I specialized the template like this:
template <typename A, typename B>
struct Foo;
template <typename A, typename B1>
struct Foo<A, typename A::template Bar<B1>> {
/* use both A and B1*/
};
template <typename A, typename B1>
struct Foo<A, typename A::template Xyz<B1>> {
/* use both A and B1*/
};
However this results (Visual Studio 2019) in
Error C2764: 'B1': template parameter not used or deducible in partial specialization 'Foo<A,A::Bar<B1>>' (5, 47)
I assume this is because I used the template parameter A as a qualifier in the specialication (typename A::template Bar<B1>).
Is there any way to circumvent this and use parameters in template specializations as qualifiers?
Note: In my usecase the first parameter is never really specialized.
Theoretically it could work to nest the specialized template in another template class (i.e. currying the metafunction), but templates can only be specialized at namespace scope.
Using a template template parameter may work out:
template <typename A, typename B>
struct Foo;
template <typename TA, template<class> class TBar, typename B1>
struct Foo<TA, TBar<B1>> {};
Given
struct A
{
template<class T>
struct Bar {};
};
you can form
Foo<A, A::Bar<int>> x;
and it will deduce A, A::Bar and int in the specialization for you. But note that no attempt is made to check that the A in A::Bar matches the A given as first template parameter; it's unclear what you'd expect to happen for, say, a Foo<double, A::Bar<int>>.
https://godbolt.org/z/hGhsZm
I assume this is because I used the template parameter A as a qualifier in the specialication (typename A::template Bar).
I don't think so.
Suppose A is as follows
struct A
{
template <typename B>
using Bar = int;
};
and that you define a Foo<A,A::Bar<B1>>.
But A::Bar<B1> is int !
So you're defining Foo<A, int>.
How can, the compiler, deduce B1 from int ?
It seems to me that it can't.
Possible solution (depending from your needs): if you need to specialize through B1, but you need A::Bar<B1> inside Foo, you can use B1 itself as second parameter and A::Bar<B1> as using type inside Foo
template <typename A, typename B1>
struct Foo<A, B1> {
using bType = A::template Bar<B1>;
};

Obtain original struct/class name during C++ template instantiation

template<typename T> struct S {};
template<typename T> struct R {};
int main() {
typedef S<double> s1;
typedef S<int> s2;
typedef R<int> s3;
static_assert(xxx<s1, s2>::value,
"No, assertion must not be raised");
static_assert(xxx<s2, s3>::value,
"Yes, assertion must be raised");
}
So, I want xxx<s1, s2>::value to return true while xxx<s2, s3>::value to return false during compile-time.
Is the existence of xxx impossible in C++?
Or, is the existence of xxx theoretically possible in C++ but possibly no one has done it yet?
Use two specialisations​ that use template template parameters to perform this "matching":
template<
typename T,
typename V>
struct xxx;
template<
template <class> class A,
template <class> class B,
typename X,
typename Y>
struct xxx<A<X>, B<Y>> {
static constexpr const int value = false;
};
template<
template <class> class U,
typename X,
typename Y>
struct xxx<U<X>, U<Y>> {
static constexpr const int value = true;
};
With your code on ideone
Note: For it to be a real type trait you should not set value manually, but derive from std::integral_constant (std::true_type or std::false_type). Above is just a quick mockup I did on my phone.
Something like same_base_template:
#include <type_traits>
template<class A, class B>
struct same_base_template : std::false_type{};
template<template<class...> class S, class... U, class... V>
struct same_base_template<S<U...>, S<V...>> : std::true_type{};
Edit:
And a third specialization since you are using non-type template arguments (std::ratio):
template<class T, template<T...> class S, T... U, T... V>
struct same_base_template<S<U...>, S<V...>> : std::true_type{};
Demo
This uses true_typeand false_type from type_traits so we don't need to write a constexpr bool value ourselves. I used a variadic template here because it was slightly more generic and took only a few more keystrokes to do. For your specific use case, you don't need them)

Currying for templates in C++ metaprogramming

This is more of a conceptual question. I'm trying to find the easiest way of converting a two-arg template (the arguments being types) into a one-arg template. I.e., binding one of the types.
This would be the meta-programming equivalent of bind in boost/std. My example includes a possible use-case, which is, passing std::is_same as template argument to a template that takes a one-arg template template argument (std::is_same being a two-arg template), i.e. to TypeList::FindIf. The TypeList is not fully implemented here, neither is FindIf, but you get the idea. It takes a "unary predicate" and returns the type for which that predicate is true, or void if not such type.
I have 2 working variants but the first is not a one-liner and the 2nd uses a rather verbose BindFirst contraption, that would not work for non-type template arguments. Is there a simple way to write such a one-liner? I believe the procedure I'm looking for is called currying.
#include <iostream>
template<template<typename, typename> class Function, typename FirstArg>
struct BindFirst
{
template<typename SecondArg>
using Result = Function<FirstArg, SecondArg>;
};
//template<typename Type> using IsInt = BindFirst<_EqualTypes, int>::Result<Type>;
template<typename Type> using IsInt = std::is_same<int, Type>;
struct TypeList
{
template<template<typename> class Predicate>
struct FindIf
{
// this needs to be implemented, return void for now
typedef void Result;
};
};
int main()
{
static_assert(IsInt<int>::value, "");
static_assert(!IsInt<float>::value, "");
// variant #1: using the predefined parameterized type alias as predicate
typedef TypeList::FindIf<IsInt>::Result Result1;
// variant #2: one-liner, using BindFirst and std::is_same directly
typedef TypeList::FindIf< BindFirst<std::is_same, int>::Result>::Result Result2;
// variant #3: one-liner, using currying?
//typedef TypeList::FindIf<std::is_same<int, _>>::Result Result2;
return 0;
}
Click here for code in online compiler GodBolt.
I think the typical way of doing this is keep everything in the world of types. Don't take template templates - they're messy. Let's write a metafunction named ApplyAnInt that will take a "metafunction class" and apply int to it:
template <typename Func>
struct ApplyAnInt {
using type = typename Func::template apply<int>;
};
Where a simple metafunction class might be just checking if the given type is an int:
struct IsInt {
template <typename T>
using apply = std::is_same<T, int>;
};
static_assert(ApplyAnInt<IsInt>::type::value, "");
Now the goal is to support:
static_assert(ApplyAnInt<std::is_same<_, int>>::type::value, "");
We can do that. We're going to call types that contain _ "lambda expressions", and write a metafunction called lambda which will either forward a metafunction class that isn't a lambda expression, or produce a new metafunction if it is:
template <typename T, typename = void>
struct lambda {
using type = T;
};
template <typename T>
struct lambda<T, std::enable_if_t<is_lambda_expr<T>::value>>
{
struct type {
template <typename U>
using apply = typename apply_lambda<T, U>::type;
};
};
template <typename T>
using lambda_t = typename lambda<T>::type;
So we update our original metafunction:
template <typename Func>
struct ApplyAnInt
{
using type = typename lambda_t<Func>::template apply<int>;
};
Now that leaves two things: we need is_lambda_expr and apply_lambda. Those actually aren't so bad at all. For the former, we'll see if it's an instantiation of a class template in which one of the types is _:
template <typename T>
struct is_lambda_expr : std::false_type { };
template <template <typename...> class C, typename... Ts>
struct is_lambda_expr<C<Ts...>> : contains_type<_, Ts...> { };
And for apply_lambda, we just will substitute the _ with the given type:
template <typename T, typename U>
struct apply_lambda;
template <template <typename...> class C, typename... Ts, typename U>
struct apply_lambda<C<Ts...>, U> {
using type = typename C<std::conditional_t<std::is_same<Ts, _>::value, U, Ts>...>::type;
};
And that's all you need actually. I'll leave extending this out to support arg_<N> as an exercise to the reader.
Yeah, I had this issue to. It took a few iterations to figure out a decent way to do this. Basically, to do this, we need to specify a reasonable representation of what we want and need. I borrowed some aspects from std::bind() in that I want to specify the template that I wish to bind and the parameters that I want to bind to it. Then, within that type, there should be a template that will allow you to pass a set of types.
So our interface will look like this:
template <template <typename...> class OP, typename...Ts>
struct tbind;
Now our implementation will have those parameters plus a container of types that will be applied at the end:
template <template <typename...> class OP, typename PARAMS, typename...Ts>
struct tbind_impl;
Our base case will give us a template type, which I'll call ttype, that'll return a template of the contained types:
template <template <typename...> class OP, typename...Ss>
struct tbind_impl<OP, std::tuple<Ss...>>
{
template<typename...Us>
using ttype = OP<Ss...>;
};
Then we have the case of moving the next type into the container and having ttype refer to the ttype in the slightly simpler base case:
template <template <typename...> class OP, typename T, typename...Ts, typename...Ss>
struct tbind_impl<OP, std::tuple<Ss...>, T, Ts...>
{
template<typename...Us>
using ttype = typename tbind_impl<
OP
, std::tuple<Ss..., T>
, Ts...
>::template ttype<Us...>;
};
And finally, we need a remap of the templates that will be passed to ttype:
template <template <typename...> class OP, size_t I, typename...Ts, typename...Ss>
struct tbind_impl<OP, std::tuple<Ss...>, std::integral_constant<size_t, I>, Ts...>
{
template<typename...Us>
using ttype = typename tbind_impl<
OP
, typename std::tuple<
Ss...
, typename std::tuple_element<
I
, typename std::tuple<Us...>
>::type
>
, Ts...
>::template ttype<Us...>;
Now, since programmers are lazy, and don't want to type std::integral_constant<size_t, N> for each parameter to remap, we specify some aliases:
using t0 = std::integral_constant<size_t, 0>;
using t1 = std::integral_constant<size_t, 1>;
using t2 = std::integral_constant<size_t, 2>;
...
Oh, almost forgot the implementation of our interface:
template <template <typename...> class OP, typename...Ts>
struct tbind : detail::tbind_impl<OP, std::tuple<>, Ts...>
{};
Note that tbind_impl was placed in a detail namespace.
And voila, tbind!
Unfortunately, there is a defect prior to c++17. If you pass tbind<parms>::ttype to a template that expects a template with a particular number of parameters, you will get an error as the number of parameters don't match (specific number doesn't match any number). This complicates things slightly requiring an additional level of indirection. :(
template <template <typename...> class OP, size_t N>
struct any_to_specific;
template <template <typename...> class OP>
struct any_to_specific<OP, 1>
{
template <typename T0>
using ttype = OP<T0>;
};
template <template <typename...> class OP>
struct any_to_specific<OP, 2>
{
template <typename T0, typename T1>
using ttype = OP<T0, T1>;
};
...
Using that to wrap tbind will force the compiler to recognize the template having the specified number of parameters.
Example usage:
static_assert(!tbind<std::is_same, float, t0>::ttype<int>::value, "failed");
static_assert( tbind<std::is_same, int , t0>::ttype<int>::value, "failed");
static_assert(!any_to_specific<
tbind<std::is_same, float, t0>::ttype
, 1
>::ttype<int>::value, "failed");
static_assert( any_to_specific<
tbind<std::is_same, int , t0>::ttype
, 1
>::ttype<int>::value, "failed");
All of which succeed.

Detect same class inheritance with SFINAE

I'm trying to write a metafunction that checks whether all types passed as a variadic template parameter are distinct. It seems that the most performant way to do this is to inherit from a set of classes and detect, whether there is an error.
The problem is that compilation fails in the following code, while I would expect SFINAE to work.
Edit. The question is not "how to write that metafunction" but "how do I catch that double inheritance error and output false_type when it happens". AFAIK, it's possible only with SFINAE.
template <typename T>
struct dummy {};
// error: duplicate base type ‘dummy<int>’ invalid
template <typename T, typename U>
struct fail : dummy<T>, dummy<U> {};
template <typename T>
true_type test(fail<T, T> a = fail<T, T>());
false_type test(...);
int main() {
cout << decltype(test<int>())::value << endl;
}
Live version here.
Edit. Previously I've tried to do this with specialization failure, but it didn't work either with the same compilation error.
template <typename T>
struct dummy {};
template <typename T, typename U>
struct fail : dummy<T>, dummy<U>, true_type {};
template <typename T, typename U = void>
struct test : false_type {};
template <typename T>
struct test<T, typename enable_if<fail<T, T>::value, void>::type> : true_type {};
Live version here.
You can't catch duplicate inheritance with SFINAE, because it is not one of the listed reasons for deduction to fail under 14.8.2p8 [temp.deduct]; equally, it is because the error occurs outside the "immediate context" of template deduction, as it is an error with the instantiation of your struct fail.
There is however a very similar technique which is suitable for use in your case, which is to detect an ambiguous conversion from a derived class to multiple base classes. Clearly the ambiguous base classes can't be inherited directly from a single derived class, but it works fine to inherit them in a linear chain:
C<> A<int>
| /
C<int> A<char>
| /
C<char, int> A<int>
| /
C<int, char, int>
Now a conversion from C<int, char, int> to A<int> will be ambiguous, and as ambiguous conversion is listed under 14.8.2p8 we can use SFINAE to detect it:
#include <type_traits>
template<class> struct A {};
template<class... Ts> struct C;
template<> struct C<> {};
template<class T, class... Ts> struct C<T, Ts...>: A<T>, C<Ts...> {};
template<class... Ts> void f(A<Ts>...);
template<class... Ts> std::false_type g(...);
template<class... Ts> decltype(f((A<Ts>(), C<Ts...>())...), std::true_type()) g(int);
template<class... Ts> using distinct = decltype(g<Ts...>(0));
static_assert(distinct<int, char, float>::value, "!!");
static_assert(!distinct<int, char, int>::value, "!!");
THE ERROR
prog.cpp: In instantiation of ‘struct fail<int, int>’:
prog.cpp:23:29: required from here
prog.cpp:9:8: error: duplicate base type ‘dummy<int>’ invalid
struct fail : dummy<T>, dummy<U> {};
As stated in the above diagnostic you cannot explicitly inherit from the same base more than once in a base-specifier-list, and since T and U can possibly be of the same type.. BOOM.
WAIT, HOLD UP; WHAT ABOUT SFINAE?
SFINAE is only checked in the immediate context of the template itself, error that happens beyond the declaration are not suitable to trigger a SFINAE.
14.8.2p8 Template argument deduction [temp.deduct]
If a substitution results in an invalid type or expression, type deduction fails. An invalid type or expression is one that would be ill-formed, with a diagnostic required, if written using the substituted arguemts.
Only invalid types and expressions in the immediate context of the function type and its templat eparameter types can result in a deduction failure.
The ill-formed inheritance does not happen in the immediate context, and the application is ill-formed.
To answer your question explicitly: since inheritance never happens in the declaration of a certain function, the ill-formed inheritance itself cannot be caught by SFINAE.
Sure, you can ask for the compiler to generate a class that uses inheritance, by instantiation it in the function declaration, but the actual (ill-formed) inheritance is not in the immediate context.
As I understand, you want a traits to check if all type are differents,
following may help:
#include <type_traits>
template <typename T, typename ...Ts> struct is_in;
template <typename T> struct is_in<T> : std::false_type {};
template <typename T1, typename T2, typename ... Ts>
struct is_in<T1, T2, Ts...> : std::conditional<std::is_same<T1, T2>::value, std::true_type, is_in<T1, Ts...>>::type {};
template <typename ... Ts> struct are_all_different;
template <> struct are_all_different<> : std::true_type {};
template <typename T> struct are_all_different<T> : std::true_type {};
template <typename T1, typename T2, typename ... Ts> struct are_all_different<T1, T2, Ts...> :
std::conditional<is_in<T1, T2, Ts...>::value, std::false_type, are_all_different<T2, Ts...>>::type {};
static_assert(are_all_different<char, int, float, long, short>::value, "type should be all different");
static_assert(!are_all_different<char, int, float, char, short>::value, "type should not all different");
What about a simple std::is_same<>? As far as I can see, it directly models the desired behaviour of your class.
So try something like this:
template<typename T, typename U>
fail
{
fail(const T &t, const U &u)
{
static_assert(std::is_same<T,U>::value,"T and U must have a distinct type");
}
};
Or even better, directly use std::is_same<T,U> in your code.
EDIT: Here is a solution inspired by Jarod42's but which uses only a single class (to make it clearer: this is an answer to the question how to write a variadic class template which detects whether all given types are distinct, which seemed to be the desired goal in one of the early versions of the original question):
#include <type_traits>
template <typename ...Ts> struct are_all_different {};
template <> struct are_all_different<> {static const bool value=true;};
template <typename T> struct are_all_different<T> {static const bool value=true;};
template <typename T1, typename T2>
struct are_all_different<T1, T2>
{
static const bool value = !std::is_same<T1, T2>::value;
};
template <typename T1, typename T2, typename ...Ts>
struct are_all_different<T1,T2,Ts...>
{
static const bool value = are_all_different<T1, T2>::value
&& are_all_different<T1, Ts...>::value
&& are_all_different<T2, Ts...>::value;
};
static_assert(are_all_different<char, int, float, long, short>::value, "type should be all different");
static_assert(!are_all_different<char, int, float, char, short>::value, "type should not all different");
In the end, for n variadic template arguments, this should check for equality of all of the n*(n+1)/2 combinations.
I don’t really understand what you are trying to achieve, but I can help you with the error
template <typename T, typename U>
struct fail : dummy<T>, dummy<U> {};
template <typename T>
struct fail<T, T> : dummy<T> {};
The error is because when you instantiate fail with T and U the same you basically inherit from the same class twice, which is illegal, so you need to create a specialization to take care of this case.

C++ type traits to select between T1 and T2

I want a template to select from two types based on some condition. E.g.
struct Base {};
template <typename T1, typename T2>
struct test
{
// e.g. here it should select T1/T2 that is_base_of<Base>
typename select_base<T1, T2>::type m_ValueOfBaseType;
};
Of course to pass condition to the select_base (to make it generic) would be useful, but hard-coded solution is easier and good as well.
Here's a sample solution that I tried but it always selects T1: http://ideone.com/EnVT8
The question is how to implement the select_base template.
If you use std::conditional instead of if_ class template as implemented by #Matthieu in his answer, then your solution would reduce to this:
template <typename T, typename U>
struct select_base
{
typedef typename std::conditional<std::is_base_of<T, Base>::value, T, U>::type base_type;
};
Or simply this:
template <typename T, typename U>
struct select_base : std::conditional<std::is_base_of<T, Base>::value, T, U> {};
which looks even better.
The difference between these two solutions is that in the first solution you give a programmer-friendly name to the nested type, as I've given it base_type, while in the second solution the nested type is just type which doesn't look that programmer-friendly.
Note that in both of the above solutions, you've to use the nested type as either select_base<T,U>::base_type (in the first solution) or select_base<T,U>::type (in the second solution — and because of that, if you've use typename as you've written yourself in the question itself.
However, if you instead use template alias, defined as:
template<typename T, typename U>
using base_type = typename std::conditional<std::is_base_of<T, Base>::value, T, U>::type;
then you can use base_type<T,U> without any nested-type and typename as:
template <typename T1, typename T2>
struct test
{
//typename select_base<T1, T2>::type m_ValueOfBaseType; //ugly!
base_type<T1, T2> m_ValueOfBaseType; //better
};
Hope that helps.
C++14 (and onwards):
template <typename T, typename U>
struct select_base:
std::conditional_t<std::is_base_of<T, Base>::value, T, U> {};
In the same vein, you can instead use this:
template<typename T, typename U>
using select_base = std::conditional_t<std::is_base_of_v<T,Base>, T, U>;
The difference between these two approaches can be observed when you use them. For example, in the first case if you have to use ::type whereas in the second, you dont. And if any dependent type involves in the usage of the first approach, you have to use typename as well, to assist the compiler. The second approach is free of all such noises and thus superior to the rest of the approaches in this answer.
Also, please note that you can write similar type-alias in C++11 as well.
C++11:
template <typename T, typename U>
struct select_base:
std::conditional<std::is_base_of<T, Base>::value, T, U>::type {};
// ^ ^~~~~~
C++98:
Conditional is easy enough:
template <typename bool, typename T, typename U>
struct conditional { typedef T type; };
template <typename T, typename U>
struct conditional<false, T, U> { typedef U type; };
is_base_of is slightly more complicated, an implementation is available in Boost that I will not reproduce here.
Afterwards, see C++11.