Сначала какой-то код, затем какой-то контекст, затем вопрос:
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
ожидает, что a 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
(что на практике гораздо более полезно). Что касается clang, я действительно не могу сказать.
На практике кажется, что у меня нет другого выхода, кроме как пойти с 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, и в этом случае я должен использовать псевдонимы напрямую, потому что структуры не должны создаваться. Чтобы получить представление, часть фактического кода здесь.