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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With