Проверьте, содержит ли пакет параметров C ++ 0x тип - PullRequest
6 голосов
/ 22 января 2010

Мне было интересно, предоставляет ли C ++ 0x какие-либо встроенные возможности, чтобы проверить, содержит ли пакет параметров шаблона с переменным числом определенный тип. Сегодня, boost ::: mpl :: contains может использоваться для достижения этой цели, если вы используете boost :: mpl :: vector в качестве замены собственно шаблонов переменных. Тем не менее, он имеет серьезные накладные расходы во время компиляции. Я предполагаю, что C ++ 0x имеет поддержку уровня компилятора для std :: is_same. Поэтому я подумал, что обобщение, подобное приведенному ниже, также поддерживается в компиляторе.

template <typename... Args, typename What>
struct is_present
{
  enum { value = (What in Args...)? 1 : 0 };
};

Ответы [ 3 ]

4 голосов
/ 23 января 2010

Нет, вы должны использовать (частичную) специализацию с вариационными шаблонами для выполнения вычислений во время компиляции следующим образом:

#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, который вычисляет длину списка параметров, например ::1004*

template < typename... Types >
struct typelist_len
{
   const static size_t value = sizeof...(Types);
};

Откуда вы взяли "у него серьезные накладные расходы во время компиляции" с boost mpl от? Я надеюсь, что вы не просто делаете предположения здесь. Boost mpl использует такие методы, как создание ленивых шаблонов, чтобы попытаться сократить время компиляции вместо взрыва, как это делает наивное шаблонное метапрограммирование.

2 голосов
/ 23 января 2010

Если вы хотите избежать ручной рекурсии типа, 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 > {};
1 голос
/ 24 января 2019

К счастью, стандарт 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;
    }
}

Примечание: это было взято из другого вопроса , который был помечен как дубликат этого вопроса. Поскольку это «канонический» вопрос для этой темы, я добавил эту важную информацию здесь.

...