Для gcc 5.0 и clang 3.6 требуется typename
Ключевое слово в следующем примере:
template<typename T>
struct B
{
typedef int Type;
};
template<int n>
struct A
{
typedef typename B<decltype(throw (int*)n)>::Type Throw;
typedef typename B<decltype(delete (int*)n)>::Type Delete;
};
На это распространяется следующая формулировка в стандарте C ++ 11:
[За исключением] / 2Выражение броска имеет тип void.
[Expr.delete] / 1Операнд должен иметь указатель на тип объекта или тип класса, имеющий одно неявное преобразование
функция указателя на тип объекта. Результат имеет тип void.
Итак, я предполагаю decltype
производит void
в обоих случаях.
[Expr.const] / 2Условное выражение является основным константным выражением, если оно не включает одно из следующих
оцениваемое подвыражение
новое выражение
выражение броска
Это говорит о том, что выражение, включающее либо throw
или же delete
не может быть постоянным выражением.
[Temp.dep.type] / 8Тип зависит, если он
простой-шаблон-идентификатор, в котором либо имя шаблона является параметром шаблона, либо любым из шаблона
Аргументы являются зависимым типом или выражением, которое зависит от типа или значенияобозначается
decltype(expression)
где выражение зависит от типа
Так B<decltype(..)>
зависит, только если выражение зависит от типа.
[Temp.dep.expr] / 4Выражения следующих форм никогда не зависят от типа (потому что тип выражения не может быть
зависимая):delete cast-expression throw assignment-expression
Это говорит о том, что ни одно выражение не может зависеть от типа.
Gcc и clang не правы?
Давайте вернемся к тому, когда typename
необходимо. §14.6 [temp.res] / p3, все цитаты взяты из N4140:
Когда Квалифицированный-идентификатор предназначен для ссылки на тип, который не является
член текущего экземпляра (14.6.2.1) и его
вложенное имя спецификатор относится к зависимому типу, он должен начинаться с ключевого словаtypename
, образуя имяТипа спецификатор.
Квалифицированный-идентификатор в этом случае B<decltype(throw (int*)n)>::Type
(и delete
версия, для которой анализ точно такой же). Так typename
требуется, если вложенное имя спецификатор, или же B<decltype(throw (int*)n)>::
относится к зависимому типу.
В §14.6.2.1 [temp.dep.type] / p8 сказано, что шесть не связанных между собой пуль не указано, что
Тип зависит, если он
[…](8.7) простой шаблон-идентификатор в котором либо имя шаблона является
Параметр шаблона или любой из аргументов шаблона является зависимым
тип или выражение, которое зависит от типа или значения, или(8.8) — обозначается
decltype(
выражение)
, где выражение зависит от типа (14.6.2.2).
B<decltype(throw (int*)n)>
это простой шаблон-идентификатор. Название шаблона, B
, не является параметром шаблона. Единственный аргумент шаблона, decltype(throw (int*)n)
, это не выражение, так B<decltype(throw (int*)n)>
зависит только если decltype(throw (int*)n)
это зависимый тип. decltype(throw (int*)n)
в свою очередь, в соответствии с пулей 8.8, зависит только, если throw (int*)n
зависит от типа. Но мы знаем, что согласно §14.6.2.2 [temp.dep.expr] / p4:
Выражения следующих форм никогда не зависят от типа (потому что
[…]
тип выражения не может быть зависимым):[…]
::
выбиратьdelete
монолитно-выражение[…]
throw
Назначение выражениевыбирать
Следовательно, throw (int*)n
не зависит от типа, и так decltype(throw (int*)n)
это не зависимый тип, и так B<decltype(throw (int*)n)>
это не зависимый тип, и так typename
не требуется для B<decltype(throw (int*)n)>::Type
и так да, это ошибка компилятора.