Проверьте, содержит ли пакет параметров C++0x тип
Мне было интересно, если C++0x предоставляет какие-либо встроенные возможности, чтобы проверить, содержит ли пакет параметров шаблона Variadic определенного типа. Сегодня, boost::: mpl:: contains может использоваться для достижения этой цели, если вы используете boost:: mpl:: vector в качестве замены собственно шаблонов variadic. Тем не менее, он имеет серьезные накладные расходы во время компиляции. Я полагаю, C++0x имеет поддержку уровня компилятора для std::is_same. Поэтому я подумал, что в компиляторе также поддерживается обобщение, подобное приведенному ниже.
template <typename... Args, typename What>
struct is_present
{
enum { value = (What in Args...)? 1 : 0 };
};
4 ответа
Нет, вы должны использовать (частичную) специализацию с переменными шаблонами для выполнения вычислений во время компиляции следующим образом:
#include <type_traits>
template < typename Tp, typename... List >
struct contains : std::true_type {};
template < typename Tp, typename Head, typename... Rest >
struct contains<Tp, Head, Rest...>
: std::conditional< std::is_same<Tp, Head>::value,
std::true_type,
contains<Tp, Rest...>
>::type {};
template < typename Tp >
struct contains<Tp> : std::false_type {};
Существует только одна внутренняя операция для шаблонов с переменными параметрами - это специальная форма оператора sizeof, который вычисляет длину списка параметров, например:
template < typename... Types >
struct typelist_len
{
const static size_t value = sizeof...(Types);
};
Откуда вы взяли "у него серьезные накладные расходы во время компиляции" с boost mpl? Я надеюсь, что вы не просто делаете предположения здесь. Boost mpl использует такие методы, как создание ленивых шаблонов, чтобы попытаться сократить время компиляции вместо взрыва, как это делает наивное шаблонное метапрограммирование.
К счастью, стандарт C++ развился. С C++1z или C++17 вы, наконец, можете легко перебирать пакеты параметров. Таким образом, код для ответа (почти) так же прост, как предложено в вопросе:
template<typename ... Args, typename What>
struct is_prsent {
static constexpr bool value {(std::is_same_v<What, Args> || ...)}; };
Странный вид (std::is_same_v<What, Args> || ...)
расширяется компилятором внутри (std::is_same_v<What, Args[0]> || std::is_same_v<What, Args[1]> || ...)
, который именно то, что вы хотите. Это даже правильно дает false
даже с пустым Args
пакет параметров.
Можно даже выполнить всю встроенную проверку в функции или методе - вспомогательные структуры больше не требуются:
template<typename T, typename ... List>
void foo(T t, List ... lst)
{
if constexpr((std::is_same_v<T, List> || ...)) {
std::cout << "T is in List" << std::endl;
} else {
std::cout << "T is not in List" << std::endl;
}
}
Примечание: это было взято из другого вопроса, который был отмечен как дубликат этого вопроса. Поскольку это "канонический" вопрос для этой темы, я добавил эту важную информацию здесь.
Если вы хотите избежать ручной рекурсии типа, std::common_type
мне кажется, что это единственная утилита в STL, которая является шаблоном с вариациями и, следовательно, единственной, которая потенциально может инкапсулировать рекурсию.
Решение 1
std::common_type
находит наименее производный тип в наборе типов. Если мы отождествляем числа с типами, особенно большие числа с менее производными типами, он находит наибольшее число в наборе. Затем мы должны отобразить равенство с типом ключа на уровень деривации.
using namespace std;
struct base_one { enum { value = 1 }; };
struct derived_zero : base_one { enum { value = 0 }; };
template< typename A, typename B >
struct type_equal {
typedef derived_zero type;
};
template< typename A >
struct type_equal< A, A > {
typedef base_one type;
};
template< typename Key, typename ... Types >
struct pack_any {
enum { value =
common_type< typename type_equal< Key, Types >::type ... >::type::value };
};
Решение 2
Мы можем взломать common_type
немного больше. Стандарт говорит
Программа может специализировать эту черту, если хотя бы один параметр шаблона в специализации является пользовательским типом.
и точно описывает, что внутри него: случай рекурсивной частичной специализации, случай, в котором применяется двоичный оператор, и случай терминала. По сути, это общий fold
функция, и вы можете добавить любую бинарную операцию, пожалуйста. Здесь я использовал дополнение, потому что оно более информативно, чем OR. Обратите внимание, что is_same
возвращает integral_constant
,
template< typename Addend >
struct type_sum { // need to define a dummy type to turn common_type into a sum
typedef Addend type;
};
namespace std { // allowed to specialize this particular template
template< typename LHS, typename RHS >
struct common_type< type_sum< LHS >, type_sum< RHS > > {
typedef type_sum< integral_constant< int,
LHS::type::value + RHS::type::value > > type; // <= addition here
};
}
template< typename Key, typename ... Types >
struct pack_count : integral_constant< int,
common_type< type_sum< is_same< Key, Types > > ... >::type::type::value > {};
Начиная с C++17, вы можете наследовать отс обычным расширением упаковкиstd::is_same<What, Args>...
который выполняет логическоеOR
между всемиstd::is_same
с. Ваша типовая черта будет иметьstatic constexpr bool value
переменная-член, содержащая результат. Это отличается от выражения сгиба над||
поскольку это сокращает создание остальных частейis_same<>::value
s, если совпадение найдено. Вы можете увидеть, как это работает в реализации C++11/14 ниже.
#include <type_traits>
template<class What, class... Args>
struct is_present : std::disjunction<std::is_same<What, Args>...> {};
Используя C++11 или C++14, вы можете определить свои собственныеdisjunction
с использованиемstd::conditional
и используйте эту черту типа вместоstd::disjunction
при определенииis_present
:
template<class...> struct disjunction : std::false_type {};
template<class T> struct disjunction<T> : T {};
template<class T, class... Ts>
struct disjunction<T, Ts...> :
std::conditional<bool(T::value),
T, // no `is_same<>::value` instantiations for the Ts...
disjunction<Ts...>>::type {};
Начиная с C++14, вы также можете создать шаблон вспомогательной переменной:
template<class... Ts>
constexpr bool is_present_v = is_present<Ts...>::value;
int main() {
std::cout << is_present_v<int, double, int> << '\n'; // prints 1
}