Оба clang 3.6 и gcc 5.0 требуют typename
в следующем примере:
template<typename T>
struct B
{
typedef int Type;
};
void f(int);
template<int n>
struct A
{
typedef typename B<decltype(f(n))>::Type Type;
};
На это распространяется следующая формулировка в стандарте C ++ 11:
[Temp.dep.type] / 5Имя это член неизвестной специализации если это
[Temp.dep.type] / 8
- Квалифицированный идентификатор, в котором спецификатор вложенного имени называет зависимый тип, который не является текущим
конкретизации.Тип зависит, если он
член неизвестной специализации,
простой-шаблон-идентификатор, в котором либо имя шаблона является параметром шаблона, либо любым из шаблона
Аргументы являются зависимым типом или выражением, которое зависит от типа или значенияобозначается
decltype(expression)
где выражение зависит от типа
Это предполагает B<decltype(f(n))>::Type
зависит от типа, только если B<decltype(f(n))>
зависит от типа. И что B<decltype(f(n))>
зависит только если f(n)
зависит от типа.
[Temp.dep.expr] / 1За исключением случаев, описанных ниже, выражение зависит от типа, если любое подвыражение зависит от типа.
[Temp.dep.expr] / 3Выражение id зависит от типа, если оно содержит
идентификатор, связанный с поиском имени с одним или несколькими объявлениями, объявленными с зависимым типом,
идентификатор шаблона, который зависит,
идентификатор функции преобразования, который указывает зависимый тип, или
спецификатор вложенного имени или квалифицированный идентификатор, который именует члена неизвестной специализации;
или если он называет статический член данных текущего экземпляра, который имеет тип «массив неизвестной границы
Т »для некоторых Т
Это предполагает f(n)
зависит от типа, только если n
зависит от типа, и что n
является не зависящий от типа.
Я что-то упустил, или это ошибка компилятора?
Ваш анализ немного неполон, но в остальном правильный.
Когда Квалифицированный-идентификатор предназначен для ссылки на тип, который не является
член текущего экземпляра (14.6.2.1) и его
вложенное имя спецификатор относится к зависимому типу, он должен начинаться с ключевого словаtypename
, образуя имяТипа спецификатор.
очевидно B<…>::Type
не может ссылаться на члена текущего экземпляра. Так что вопрос в том, B<decltype(f(n))>
это зависимый тип.
Тип зависит, если он
- […]
- член неизвестной специализации,
- […]
- простой шаблон-идентификатор в котором либо имя шаблона является параметром шаблона, либо любой из аргументов шаблона является зависимым
тип или выражение, которое зависит от типа или значения, или- обозначается
decltype(expression)
, где выражение зависит от типа (14.6.2.2).
f(n)
не зависит от типа, так как нет подвыражения, следовательно decltype(f(n))
не зависимый тип.
Также, decltype(...)
не может быть зависимым от значения, так как не является константным выражением1 и никакой другой параграф в [temp.dep.constexpr] не применяется. очевидно decltype(f(n))
также не зависит от типа.
И минимум, но не последний, B<decltype(f(n))>::Type
будет членом неизвестной специализации, только если аргумент шаблона является зависимым типом2, что мы пришли к выводу, что это не так.
Таким образом, по моей интерпретации, компиляторы неверны и typename
Ключевое слово не обязательно.
1 По факту, decltype(…)
это не выражение вообще.
2 [Temp.dep.type] / 5:
Имя является членом неизвестной специализации, если оно
- Квалифицированный-идентификатор в котором вложенное имя спецификатор называет зависимый тип это не текущее воплощение.
- Квалифицированный-идентификатор в котором вложенное имя спецификатор относится к текущей реализации, текущая инстанция имеет хотя бы одну
зависимый базовый класс, и название поиска Квалифицированный-идентификатор не
найти любого члена класса, который является текущим экземпляром или
его независимый базовый класс.- ID-выражение обозначение члена в выражении доступа к члену класса (5.2.5), в котором либо
- […]