Вариативные псевдонимы шаблона в качестве аргументов шаблона
Сначала некоторый код, затем некоторый контекст, затем вопрос:
template <typename T> using id = T;
template <template <typename...> class F, typename... T>
using apply1 = F <T...>;
template <template <typename...> class F>
struct apply2
{
template <typename... T>
using map = F <T...>;
};
// ...
cout << apply1 <id, int>() << endl;
cout << apply2 <id>::map <int>() << endl;
И clang 3.3, и gcc 4.8.1 компилируют это без ошибок, применяя метафункцию идентификации к int
поэтому оба выражения имеют значение по умолчанию int
(нуль).
Дело в том, что id
это template <typename>
в то время как apply1
, apply2
ожидать template <typename...>
действительно касается меня в первую очередь. Тем не менее, это очень удобно, что этот пример работает, потому что в противном случае метафункции, такие как apply1
, apply2
должно быть гораздо более вовлеченным.
С другой стороны, такие псевдонимы шаблонов вызывают серьезные проблемы в реальном коде, которые я не могу воспроизвести здесь: частые внутренние ошибки компилятора для gcc и менее частое непредвиденное поведение для clang (только в более сложных тестах SFINAE).
После нескольких месяцев проб и ошибок я теперь устанавливаю и пробую код на (экспериментальной) gcc 4.9.0, и здесь появляется ошибка:
test.cpp: In instantiation of ‘struct apply2<id>’:
test.cpp:17:22: error: pack expansion argument for non-pack parameter ‘T’ of alias template ‘template<class T> using id = T’
using map = F <T...>;
^
Итак, кажется, что этот код был недействителен все это время, но gcc зависал различными способами вместо сообщения об ошибке. Интересно, а пока apply1
, apply2
кажется эквивалентным, ошибка сообщается только для apply2
(что гораздо полезнее на практике). Что касается лязга, я действительно не могу сказать.
На практике кажется, что у меня нет другого пути, кроме как перейти с gcc 4.9.0 и исправить код, даже если он станет намного сложнее.
Теоретически я хотел бы знать, что говорится в стандарте: допустим ли этот код? Если нет, является ли использование apply1
инвалид тоже? или только apply2
?
РЕДАКТИРОВАТЬ
Просто чтобы уточнить, что все проблемы, которые у меня были до сих пор, относятся к псевдонимам шаблонов, а не к шаблонным структурам. Например, рассмотрим следующую модификацию:
template <typename T> struct id1 { using type = T; };
// ...
cout << typename apply1 <id1, int>::type() << endl;
cout << typename apply2 <id1>::map <int>::type() << endl;
Это хорошо компилирует и печатает 0
в обоих случаях на clang 3.3, gcc 4.8.1, gcc 4.9.0.
В большинстве случаев мои обходные пути представляли промежуточную структуру шаблона перед псевдонимом. Однако сейчас я пытаюсь использовать метафункции для параметризации общих тестов SFINAE, и в этом случае мне приходится использовать псевдонимы напрямую, потому что структуры не должны создаваться. Просто чтобы получить представление, фрагмент реального кода здесь.
1 ответ
ISO C++11 14.3.3/1:
Аргумент шаблона для параметра шаблона шаблона должен быть именем шаблона класса или шаблона псевдонима, выраженным как id-выражение.
Кроме того, я не вижу особых исключений для параметров шаблона шаблона.
С другой стороны, такие псевдонимы шаблонов вызывают серьезные проблемы в реальном коде, которые я не могу воспроизвести здесь: частые внутренние ошибки компилятора для gcc и менее частое непредвиденное поведение для clang (только в более сложных тестах SFINAE).
Корень проблем может быть в других местах. Вы должны попытаться локализовать код, который вызывает внутреннюю ошибку компилятора - просто удалите несвязанные части одну за другой (или используйте какой-нибудь бинарный поиск, то есть "разделяй и властвуй") - и проверьте, присутствует ли ошибка на каждом этапе.
Что касается ошибки GCC 4.9.0, попробуйте изменить
template <typename... T>
using map = F <T...>;
в
template <typename... U>
using map = F <U...>;
Может быть, это поможет понять, что видит GCC.