C++ type traits to select between T1 and T2

孤街醉人 提交于 2019-12-06 19:47:59

问题


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.


回答1:


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.




回答2:


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.



来源:https://stackoverflow.com/questions/11065313/c-type-traits-to-select-between-t1-and-t2

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!