Может ли выражение throw или delete быть зависимым?

И 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

Выражение throw имеет тип void.

[выраж.удалить]/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 неправильными?


person willj    schedule 30.12.2014    source источник
comment
Я не думаю, что ваши рассуждения о [temp.dep.constexpr]/p1 верны. reinterpret_cast также не может появляться в константном выражении, но [temp.dep.constexpr]/p3 явно указывает, что выражение, включающее reinterpret_cast, возможно, может зависеть от значения.   -  person T.C.    schedule 31.12.2014
comment
decltype(..) не является выражением. Так что вам также нужен [temp.dep.type]/9.8. Этот абзац требует только, чтобы выражение в decltype(expression) не было зависимым от типа, что хорошо указано для throw и new.   -  person dyp    schedule 31.12.2014
comment
@dyp Да! Это кажется убедительным.   -  person willj    schedule 31.12.2014


Ответы (1)


Вернемся к тому, когда требуется typename. §14.6 [temp.res]/p3, все цитаты из N4140:

Когда квалифицированный-id предназначен для ссылки на тип, который не является членом текущего экземпляра (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) — simple-template-id, в котором либо имя шаблона является параметром шаблона, либо любой из аргументов шаблона является зависимым типом или выражением, зависящим от типа или значения, или же

(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:

Выражения следующих форм никогда не зависят от типа (поскольку тип выражения не может быть зависимым):

[...]

::opt delete приведение-выражение

[...]

throw выражение-назначенияopt

[...]

Следовательно, throw (int*)n не зависит от типа, и поэтому decltype(throw (int*)n) не является зависимым типом, и поэтому B<decltype(throw (int*)n)> не является зависимым типом, и поэтому typename не требуется для B<decltype(throw (int*)n)>::Type, так что да, это ошибка компилятора.

person T.C.    schedule 30.12.2014
comment
Сегодня я, наконец, понял, в чем моя ошибка, и обнаружил, что она упоминается прямо в вашем ответе. decltype(…) не является выражением. Я никогда не сомневался в этом, и меня раздражал [temp.dep.constexpr]/1. :) - person Columbo; 31.12.2014