Является ли законным частично специализировать внутренний шаблон вариационного шаблона с аргументами из вариационного шаблона внешнего класса
Рассмотрим код:
#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, но без проблем...
Ответы
Ответ 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, хотя)