Частичная специализация содержимого кортежа с переменными аргументами - PullRequest
5 голосов
/ 20 июля 2011

В настоящее время я пытаюсь заставить некоторый код по-разному реагировать на разные типы.Это не точный код, но он передает сообщение.

template<class A, class B>
struct alpha {
  enum { value = 0 };
};

template<class T, class... Args>
struct alpha<std::tuple<Args...>, T> {
  enum { value = 1 };
};

// This gets ignored
template<class T, class... Args>
struct alpha<std::tuple<Args..., std::vector<T> >, T> {
  enum { value = 2 };
};

// This gets ignored
template<class T, class... Args>
struct alpha<std::tuple<Args..., T>, T> {
  enum { value = 3 };
};

template<class T, class... Args>
struct alpha<T, std::tuple<Args...> > {
  enum { value = 4 };
};

template<class... LArgs, class... RArgs>
struct alpha<std::tuple<LArgs...>, std::tuple<RArgs...> > {
  enum { value = 5 };
};

int main(int argc, char* argv[]) {
  std::cout << alpha<std::tuple<int, double>, double>::value << std::endl; // prints 1
  return 0;
}

Я пробовал больше, чем этот код, но пока ничего не работает, и я столкнулся с проблемой с явной специализацией вобласть без пространства имен.Для справки, я работаю над gcc 4.6 (тот, который поставляется с сервером oneiric), который, я считаю, имеет полную поддержку шаблонов с переменным числом аргументов.Мне все равно, насколько уродливым становится, если реализация работает для определения последнего аргумента пакета параметров, а также других типов.Любые предложения?

РЕДАКТИРОВАТЬ: Я хотел бы поделиться решением, которое я использовал на основе ответов (это пример).

template<typename T> struct tuple_last;

template<typename T, typename U, typename... Args>
struct tuple_last<std::tuple<T,U,Args...>> {
  typedef typename tuple_last<std::tuple<U,Args...>>::type type;
};

template<typename T>
struct tuple_last<std::tuple<T>> {
  typedef T type;
};

namespace details {
// default case:
template<class T, class U>
struct alpha_impl {
enum { value = 1 };
};

template<class T>
struct alpha_impl<T, T> {
enum { value = 101 };
};

template<class T>
struct alpha_impl<T, std::vector<T>> {
enum { value = 102 };
};

// and so on.
}

template<class T, class... Args>
struct alpha<std::tuple<Args...>, T>
  : details::alpha_impl<T, tuple_last<std::tuple<Args...>>;

Ответы [ 3 ]

13 голосов
/ 20 июля 2011

Если вы компилируете, используя clang , он сообщает, что (2) и (3) непригодны для использования. Предупреждение для (3), которое вы ожидаете выбрать, выглядит следующим образом:

предупреждение: частичная специализация шаблона класса содержит параметр шаблона, который не может быть выведен; эта частичная специализация никогда не будет использоваться

struct alpha<std::tuple<Args..., T>, T> {
       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

примечание: не выводимый параметр шаблона 'Args'

template<class T, class... Args>
                           ^

Почему Args не вычитается? С ++ 0x FDIS заявляет в §14.8.2.5 / 9:

Если список аргументов шаблона [тип, который указан в терминах параметров шаблона] содержит расширение пакета, которое не является последним аргументом шаблона, весь список аргументов шаблона представляет собой не выводимый контекст.

В вашей специализации тип std::tuple<Args..., T> - это тип, указанный в параметрах шаблона Args и T. Он содержит расширение пакета (Args...), но это расширение пакета не является последним аргументом шаблона (T является последним аргументом шаблона). Таким образом, весь список аргументов шаблона tuple (всего <Args..., T>) представляет собой не выводимый контекст.

Список аргументов std::tuple - единственное место в списке аргументов специализации шаблона, которое появляется Args; поскольку оно не выводится оттуда, оно вообще не выводится, и специализация никогда не будет использоваться.

Матье М. предлагает хитрый обходной путь в своем ответе .

12 голосов
/ 20 июля 2011

@ Джеймс уточнил, почему, теперь давайте попробуем найти альтернативу.

Я бы предложил использовать другой уровень косвенности.

1. Получение последнего аргумента

template <typename T> struct Last;

template <typename T, typename U, typename... Args>
struct Last<std::tuple<T,U,Args...>>
{
  typedef typename Last<std::tuple<U,Args...>>::type type;
};

template <typename T>
struct Last<std::tuple<T>>
{
  typedef T type;
};

2. Представляем специализированного помощника

template <typename T, typename U>
struct alpha_tuple
{
  enum { value = 1 };
};

template <typename T>
struct alpha_tuple<T,T>
{
  enum { value = 3 };
};

template <typename T>
struct alpha_tuple<std::vector<T>,T>
{
  enum { value = 2; }
};

3. Подключаем

template <typename T>
struct alpha<std::tuple<>, T>
{
  enum { value = 1 };
};

template <typename T, typename U, typename Args...>
struct alpha<std::tuple<U, Args...>, T>
{
  typedef typename Last<std::tuple<U, Args...>>::type LastType;
  enum { value = alpha_tuple<LastType,T>::value };
};

Обратите внимание, что для пустых кортежей нет последнего типа, поэтому мне пришлось разбираться с ними в отдельной специализации.

1 голос
/ 20 июля 2011

Если вы хотите узнать, является ли кортеж конкретным последним членом, вот типовая черта для этого:

#include <type_traits>
#include <tuple>

template <typename ...Args> struct back;
template <typename T, typename ...Args> struct back<T, Args...>
  { typedef typename back<Args...>::type type; };
template <typename T> struct back<T>
  { typedef T type; };


template <typename...> struct tuple_has_last : public std::false_type {};
template <typename T, typename... Args> struct tuple_has_last<T, std::tuple<Args...>>
{
  static const bool value = std::is_same<typename back<Args...>::type, T>::value;
};

Редактировать: О, я не видел, что Матье уже написал то же самое. Не бери в голову.

...