Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can a throw or delete expression ever be dependent?

Both gcc 5.0 and clang 3.6 require the typename keyword in the following example:

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;
};

This is covered by the following wording in the C++11 standard:

[except]/2

A throw-expression is of type void.

[expr.delete]/1

The operand shall have a pointer to object type, or a class type having a single non-explicit conversion function to a pointer to object type. The result has type void.

So I'm assuming decltype produces void in both cases.

[expr.const]/2

A conditional-expression is a core constant expression unless it involves one of the following as a potentially evaluated subexpression

  • a new-expression

  • a throw-expression

This suggests that an expression involving either throw or delete cannot be a constant expression.

[temp.dep.type]/8

A type is dependent if it is

  • a simple-template-id in which either the template name is a template parameter or any of the template arguments is a dependent type or an expression that is type-dependent or value-dependent

  • denoted by decltype(expression), where expression is type-dependent

So B<decltype(..)> is dependent only if the expression is type-dependent.

[temp.dep.expr]/4

Expressions of the following forms are never type-dependent (because the type of the expression cannot be dependent):

delete cast-expression
throw assignment-expression

This suggests that neither expression can be type-dependent.

Are gcc and clang both wrong?

like image 932
willj Avatar asked Dec 30 '14 22:12

willj


1 Answers

Let's go back to when typename is required. §14.6 [temp.res]/p3, all quotes are from N4140:

When a qualified-id is intended to refer to a type that is not a member of the current instantiation (14.6.2.1) and its nested-name-specifier refers to a dependent type, it shall be prefixed by the keyword typename, forming a typename-specifier.

The qualified-id in this case is B<decltype(throw (int*)n)>::Type (and the delete version, for which the analysis is exactly the same). So typename is required if the nested-name-specifier, or B<decltype(throw (int*)n)>::, refers to a dependent type.

§14.6.2.1 [temp.dep.type]/p8 says, with six unrelated bullets omitted, that

A type is dependent if it is

[...]

(8.7) — a simple-template-id in which either the template name is a template parameter or any of the template arguments is a dependent type or an expression that is type-dependent or value-dependent, or

(8.8) — denoted by decltype(expression), where expression is type-dependent (14.6.2.2).

B<decltype(throw (int*)n)> is a simple-template-id. The template name, B, is not a template parameter. The only template argument, decltype(throw (int*)n), is not an expression, so B<decltype(throw (int*)n)> is dependent only if decltype(throw (int*)n) is a dependent type. decltype(throw (int*)n), in turn, per bullet 8.8, is only dependent if throw (int*)n is type-dependent. But we know that, per §14.6.2.2 [temp.dep.expr]/p4:

Expressions of the following forms are never type-dependent (because the type of the expression cannot be dependent):

[...]

::optdelete cast-expression

[...]

throw assignment-expressionopt

[...]

Therefore, throw (int*)n is not type-dependent, and so decltype(throw (int*)n) is not a dependent type, and so B<decltype(throw (int*)n)> is not a dependent type, and so typename is not required for B<decltype(throw (int*)n)>::Type, and so yes, this is a compiler bug.

like image 84
T.C. Avatar answered Nov 05 '22 11:11

T.C.