Подтвердить что ты не робот

Является ли законным частично специализировать внутренний шаблон вариационного шаблона с аргументами из вариационного шаблона внешнего класса

Рассмотрим код:

#include <iostream>

template <class... Ts>
struct outer {
   template <class... ITs>
   struct inner {
      static constexpr bool value = false;
   };

   template <class... ITs>
   struct inner<Ts..., ITs...> {   
      static constexpr bool value = true;
   };
};

int main() {
   std::cout << outer<int, float, double>::inner<int, float, double, int>::value << std::endl;
}

Код компилируется с помощью clang++, но не с g++, где он создает ошибку:

temp3.cc:11:11: ошибка: аргумент пакета параметров "Ts... должен быть на конец списка аргументов шаблона

struct inner<Ts..., ITs...> {
       ^

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

Edit: Для полноты стоит добавить, что clang для вышеуказанного кода предупреждает, что у него может возникнуть проблема с выводом параметров IT, но без проблем...

4b9b3361

Ответ 1

Это ошибка gcc. Это вполне допустимая частичная специализация:

template <class... ITs>
struct inner<Ts..., ITs...> {   
   static constexpr bool value = true;
};

Выделенные пакеты параметров шаблона должны быть последними, и ITs... удовлетворяет этому. Но Ts... - это не пакет, который нужно вывести здесь, это только определенный пакет параметров.

Кроме того, gcc компилирует несколько эквивалентных формулировок:

template <class... Ts>
struct X {
    template <class... Us>
    static void foo(Ts..., Us...) { }
};

int main() {
   X<int>::foo(1, 'c');
}

и

template <class... Us>
struct A { };

template <class... Ts>
struct X {
    template <class... Us>
    static void foo(A<Ts..., Us...>) { }
};

int main() {
   X<int>::foo(A<int, char>{});
}

Они эквивалентно хорошо сформированы в вашем первоначальном примере.

Ответ 2

Верховая езда на W.F. ответ, сохраняя то же самое, что и в исходном вопросе:

#include <iostream>

template <class... Ts>
struct pack { };

template <class... Ts>
class outer {
   template <class IT>
   struct _inner {
      static constexpr bool value = false;
   };

   template <class... ITs>
   struct _inner<pack<Ts..., ITs...>> {
      static constexpr bool value = true;
   };
public:
   template <class... ITs>
   struct inner {
      static constexpr bool value = _inner<pack<ITs...>>::value;
   };    
};

int main() {
   std::cout << outer<int, float, double>::inner<int, float, double, int>::value
             << std::endl;
}

Он все еще производит предупреждение в clang, потому что специализированная версия _inner не может выводить ITs... кроме списка Ts..., ITs... (в struct _inner<pack<Ts..., ITs...>>) - однако код не требуют, чтобы ИТ были выведены отдельно из списка Ts..., ITs..., поэтому это должно быть нормально.

В g++ он компилируется без предупреждения.

Код: http://coliru.stacked-crooked.com/a/ae3b21dd847450b2

(Для решения без предупреждения также в clang: http://coliru.stacked-crooked.com/a/0c6c643c8ff5809e).

Ответ 3

Возможный простой, но эффективный обходной путь, вдохновленный ответом Барри:

#include <iostream>

template <class... Ts>
struct pack { };

template <class... Ts>
struct outer {
   template <class IT>
   struct inner {
      static constexpr bool value = false;
   };

   template <class... ITs>
   struct inner<pack<Ts..., ITs...>> {   
      static constexpr bool value = true;
   };
};

int main() {
   std::cout << outer<int, float, double>::inner<pack<int, float, double, int>>::value << std::endl;
}

(Он все еще выдаёт предупреждение в clang, хотя)