специализация вариативных шаблонов с помощью шаблонов классов

Вот проблема, с которой я столкнулся, играя с вариативными шаблонами. У меня есть код, который использует специализацию для подсчета "интересных" типов в пакете параметров, например:

template<typename... _Pp>
struct count;

template<>
struct count<>
{
  static const int value = 0;
};

// ignore uninteresting types
template<typename _First, typename... _Rest>
struct count<_First, _Rest...>
{
  static const int value = count<_Rest...>::value;
};

// add 1 for a pointer
template<typename _First, typename... _Rest>
struct count<_First*, _Rest...>
{
  static const int value = 1 + count<_Rest...>::value;
};

// add 1 for a reference
template<typename _First, typename... _Rest>
struct count<_First&, _Rest...>
{
  static const int value = 1 + count<_Rest...>::value;
};

// add 1 for an int
template<typename... _Rest>
struct count<int, _Rest...>
{
  static const int value = 1 + count<_Rest...>::value;
};

Этот код работает нормально, но у меня возникают проблемы, если я хочу использовать тот же подход для подсчета шаблонов классов:

// add 1 for a vector
template<typename... _Rest>
struct count<vector, _Rest...>
{
  static const int value = 1 + count<_Rest...>::value;
};

Приведенный выше код не может быть скомпилирован, ошибка - «ожидаемый тип, получил вектор» в строке, начинающейся с «количество структур». Я также не могу сделать что-то более простое, все шаблоны классов принимают один аргумент:

// add 1 for a class template with 1 type parameter
template<template<typename> class _First, typename... _Rest>
struct count<_First, _Rest...>
{
  static const int value = 1 + count<_Rest...>::value;
}

Этот код также не может быть скомпилирован из-за жалобы на «ожидаемый тип, снова получил '_First'» в строке, начинающейся с «struct count». Кто-нибудь знаком со способом достижения этой цели с помощью этого подхода (то есть некоторой модификации, которую я могу внести в одну или обе специализации, которые заставят их скомпилировать и выполнить желаемый расчет во время компиляции)?

РЕДАКТИРОВАТЬ: Я хочу, чтобы пакет параметров для вектора был несвязанным, как в следующем коде для простой оболочки контейнера с вариативными параметрами шаблона-шаблона, который также специализируется на std :: vector:

// pass a container as a parameter using variadic template-template 

parameter
template<typename _Tp, template<typename...> class _C>
struct success
{
  // not specialized for any container
  static const bool is_specialized = false;
  // data member of container type
  _C<_Tp> c_;
};

// partial specialization of above for std::vector
template<typename _Tp>
struct success<_Tp, std::vector>
{
  // specialized for vector
  static const bool is_specialized = true;
  // again, data member of container type
  std::vector<_Tp> c_;
};

ИЗМЕНИТЬ Похоже, что окончательный ответ заключается в том, что то, что я хочу сделать, не может быть выполнено, но я нашел способ переосмыслить проблему, чтобы я мог ее решить. Огромное спасибо тем, кто помогал.


person BD at Rivenhill    schedule 24.06.2016    source источник


Ответы (3)


Если я правильно понимаю, что вы хотите ... да, вы можете создать шаблонную структуру, которая может считать "шаблоны классов", чтобы вы могли написать что-то вроде

 count<std::vector, std::map, std::set, std::pair>::value

но вы не можете смешивать шаблоны классов и простые имена типов, поэтому вы не можете написать что-то вроде

 count<std::vector, int &, float, std::set>::value

Проблема в том, что если вы определите

 template <typename... _Pp>
    struct count;

вы можете передать ему std::vector<int>, потому что std::vector<int> - это typename, но вы не можете передать ему std::vector, потому что std::vector не typename; это template<typename...> class (или шаблон-шаблон), что это совершенно разные вещи.

Вы можете написать что-то вроде следующего struct countC

template <template<typename...> class ...>
struct countC;

template <>
struct countC<>
 { static const int value = 0; };

// ignore uninteresting templates
template<template<typename...> class F, template<typename...> class ... R>
struct countC<F, R...>
 { static const int value = countC<R...>::value; };

template <template<typename...> class ... R>
struct countC<std::vector, R...>
 { static const int value = 1 + countC<R...>::value; };

Ниже приведен рабочий полный пример, в котором я переписал ваш struct count как struct countT для подсчета выбранных типов, я добавил struct countC для подсчета выбранных «шаблонов классов» и я добавил struct countV для подсчета выбранных значений фиксированного имени типа.

#include <map>
#include <set>
#include <vector>
#include <utility>
#include <iostream>

// countC for templates

template <template<typename...> class ...>
struct countC;

template <>
struct countC<>
 { static const int value = 0; };

// ignore uninteresting templates
template<template<typename...> class F, template<typename...> class ... R>
struct countC<F, R...>
 { static const int value = countC<R...>::value; };

template <template<typename...> class ... R>
struct countC<std::vector, R...>
 { static const int value = 1 + countC<R...>::value; };

template <template<typename...> class ... R>
struct countC<std::map, R...>
 { static const int value = 1 + countC<R...>::value; };

template <template<typename...> class ... R>
struct countC<std::pair, R...>
 { static const int value = 1 + countC<R...>::value; };


// countV for for values of a fixed type

template <typename T, T ... v>
struct countV;

template <typename T>
struct countV<T>
 { static const int value = 0; };

// ignore uninteresting values
template <typename T, T f, T ... r>
struct countV<T, f, r...>
 { static const int value = countV<T, r...>::value; };

// count only int odd values
template <int f, int ... r>
struct countV<int, f, r...>
 { static const int value = (f % 2) + countV<int, r...>::value; };


// countT for typenames

template <typename...>
struct countT;

template <>
struct countT<>
 { static const int value = 0; };

// ignore uninteresting types
template <typename F, typename ... R>
struct countT<F, R...>
 { static const int value = countT<R...>::value; };

template <typename F, typename ... R>
struct countT<F*, R...>
 { static const int value = 1 + countT<R...>::value; };

template<typename F, typename ... R>
struct countT<F&, R...>
 { static const int value = 1 + countT<R...>::value; };

template<typename ... R>
struct countT<int, R...>
 { static const int value = 1 + countT<R...>::value; };


int main()
 {
   std::cout << "countC vector + map + set + pair                   = " 
      << countC<std::vector, std::map, std::set, std::pair>::value
      << std::endl;

   std::cout << "countT int + float + bool* + double& + bool + int& = " 
      << countT<int, float, bool*, double&, bool, int&>::value
      << std::endl;

   std::cout << "countV int, 1 + 4 + 4 + 5 + 7 + 10 + 11 + 16 + 15  = " 
      << countV<int, 1, 4, 4, 5, 7, 10, 11, 16, 15>::value
      << std::endl;

   std::cout << "countV long, 1 + 4 + 4 + 5 + 7 + 10 + 11 + 16 + 15 = " 
      << countV<long, 1, 4, 4, 5, 7, 10, 11, 16, 15>::value
      << std::endl;

   return 0;
 }

p.s .: извините за мой плохой английский.

person max66    schedule 05.07.2016
comment
Утверждение, что вы не можете смешивать шаблоны классов и простые имена типов, кажется здесь ключевой проблемой; Я немного просмотрел рабочий проект N3797, но не могу понять, каковы правила. Не могли бы вы указать мне на дополнительную информацию об этом ограничении? - person BD at Rivenhill; 06.07.2016
comment
@BDatRivenhill - Извините: я поправляюсь: нельзя смешивать шаблоны классов и простые имена типов в одном параметре шаблона pack. Очевидно, вы можете смешать создание класса шаблона, который ожидает одного типа имен и одного параметра шаблона. Но когда вы создаете пакет параметров, существуют разные производственные правила (если я не ошибаюсь), и пакет параметров может принимать только имена типов (typename ... Ts) или шаблоны (template <something> class ... Cs). - person max66; 06.07.2016
comment
@BDatRivenhill - Укажите мне дополнительную информацию об этом ограничении? Я полагаю, что это сомнение заслуживает еще одного вопроса и, извините, но я не умею искать в стандарте. Я читаю названный вами N3797, но он для меня слишком сложен. Я полагаю, могут иметь значение различные производственные правила (14.1) и правило соответствия в 14.3: (продолжить) - person max66; 06.07.2016
comment
@BDatRivenhill - (продолжите с правилом в 14.3) Существует три формы аргумента-шаблона, соответствующие трем формам параметра-шаблона: тип, не-тип и шаблон. Тип и форма каждого аргумента шаблона, указанного в идентификаторе шаблона, должны соответствовать типу и форме, указанным для соответствующего параметра, объявленного шаблоном в его списке параметров шаблона. Когда параметр, объявленный шаблоном, является пакетом параметров шаблона (14.5.3), он будет соответствовать нулю или более аргументов шаблона. - person max66; 06.07.2016

Как насчет этого?

// add 1 for a vector
template<typename... _Rest, typename T>
struct count<vector<T>, _Rest...>
{
  static const int value = 1 + count<_Rest...>::value;
};

И это?

// add 1 for a class template with 1 type parameter
template<template<typename> class _First, typename T, typename... _Rest>
struct count<_First<T>, _Rest...>
{ 
  static const int value = 1 + count<_Rest...>::value;
};
person md5i    schedule 24.06.2016
comment
Как и в случае с другим ответом, это связывает первый параметр вектора с каким-либо другим типом в пакете параметров, чего я не хочу. См. Правку, добавленную к исходному вопросу. - person BD at Rivenhill; 30.06.2016

Должен быть:

template<typename... _Rest, typename... T>
struct count<std::vector<T...>, _Rest...>
{
    static const int value = 1 + count<_Rest...>::value;
};

Общая версия:

template<template<typename...> class C, typename... _Rest, typename... T>
struct count<C<T...>, _Rest...>
{
    static const int value = 1 + count<_Rest...>::value;
};

Вариативная упаковка имеет значение.

person skypjack    schedule 24.06.2016
comment
Это не совсем то, что я хочу сделать из-за введения дополнительного пакета параметров T. Я хочу, чтобы параметры вектора не были связаны. См. Изменение исходного вопроса. - person BD at Rivenhill; 30.06.2016
comment
@BDatRivenhill Что? Класс шаблона не представляет тип, если вы не специализируетесь на нем. Из рабочего проекта: Шаблон определяет семейство классов или функций или псевдоним для семейства типов. Вы знаете, что std:: vector<int> и std::vector<double> - совершенно разные типы, не так ли? Вы хотите посчитать то, для чего не может существовать действующей специализации? Итак, на что вы рассчитываете в таком случае? Специализации просят тип, извините. - person skypjack; 30.06.2016
comment
Суть для меня: почему специализация struct success для компиляции std :: vector и специализация struct count для std :: vector не компилируется? Понимание этого поможет мне увидеть, возможно ли здесь достичь моей цели. - person BD at Rivenhill; 30.06.2016