Характеристики типа С++ для выбора между T1 и T2
Я хочу, чтобы шаблон выбирался из двух типов на основе некоторого условия. Например.
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;
};
Конечно, передать условие select_base (чтобы сделать его общим) было бы полезно, но жестко запрограммированное решение было бы проще и лучше.
Вот пример решения, которое я пробовал, но он всегда выбирает T1: http://ideone.com/EnVT8
Вопрос заключается в том, как реализовать шаблон select_base.
Ответы
Ответ 1
С++ 14 (и далее):
template <typename T, typename U>
struct select_base:
std::conditional_t<std::is_base_of<T, Base>::value, T, U> {};
В том же духе вы можете использовать это:
template<typename T, typename U>
using select_base = std::conditional_t<std::is_base_of_v<T,Base>, T, U>;
Различие между этими двумя подходами может наблюдаться при их использовании. Например, в первом случае, если вам нужно использовать ::type
, тогда как во втором - нет. И если какой-либо зависимый тип связан с использованием первого подхода, вы также должны использовать typename
, чтобы помочь компилятору. Второй подход свободен от всех таких шумов и, таким образом, превосходит остальные подходы в этом ответе.
Также обратите внимание, что вы можете написать аналогичный псевдоним типа в С++ 11.
С++ 11:
template <typename T, typename U>
struct select_base:
std::conditional<std::is_base_of<T, Base>::value, T, U>::type {};
// ^ ^~~~~~
С++ 98:
Условное достаточно просто:
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
немного сложнее, в Boost доступна реализация, которую я не буду воспроизводить здесь.
Затем, см. С++ 11.
Ответ 2
Если вы используете std::conditional
вместо шаблона класса if_
, как это было реализовано в @Matthieu в его ответе, тогда ваше решение уменьшится до это:
template <typename T, typename U>
struct select_base
{
typedef typename std::conditional<std::is_base_of<T, Base>::value, T, U>::type base_type;
};
Или просто это:
template <typename T, typename U>
struct select_base : std::conditional<std::is_base_of<T, Base>::value, T, U> {};
который выглядит еще лучше.
Разница между этими двумя решениями заключается в том, что в первом решении вы даете программирующее имя вложенному типу, так как я дал ему base_type
, а во втором решении вложенный тип - это просто type
который не выглядит более удобным для программистов.
Обратите внимание, что в обоих приведенных выше решениях вы должны использовать вложенный тип как select_base<T,U>::base_type
(в первом решении) или select_base<T,U>::type
(во втором решении — и из-за этого, ve используйте typename
, как вы сами написали в самом вопросе.
Однако, если вы вместо этого используете псевдоним шаблона, который определяется как:
template<typename T, typename U>
using base_type = typename std::conditional<std::is_base_of<T, Base>::value, T, U>::type;
то вы можете использовать base_type<T,U>
без каких-либо вложенных типов и typename
как:
template <typename T1, typename T2>
struct test
{
//typename select_base<T1, T2>::type m_ValueOfBaseType; //ugly!
base_type<T1, T2> m_ValueOfBaseType; //better
};
Надеюсь, что это поможет.