Can an expression throw or delete ever 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 corresponds to the following wording in the C ++ 11 standard:

[except] / 2

The expression expression is of type void.

[expr.delete] / 1

The operand must have a pointer to an object type or a class type that has one implicit conversion of function to a pointer to an object type. The result is of type void.

Therefore, I assume that decltype produces void in both cases.

[expr.const] / 2

A conditional expression is an expression of a basic constant if it is not associated with one of the following as a potentially evaluated subexpression

  • new expression

  • throw expression

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

[temp.dep.type] / 8

Type depends if it

  • A simple identifier template in which either the template name is a template parameter or any template arguments are a dependent type or expression, depending on the type or depending on the value

  • denoted by decltype(expression) , where the expression depends on the type

So, B<decltype(..)> depends only on whether the expression is type dependent.

[temp.dep.expr] / 4

The expressions of the following forms never depend on the type (since the type of the expression cannot be dependent):

 delete cast-expression throw assignment-expression 

This suggests that none of the expressions can be type dependent.

Are gcc and clang wrong?

+7
c ++ language-lawyer templates compiler-bug dependent-type
source share
1 answer

Go back when typename is required. Β§14.6 [temp.res] / p3, all citations from N4140:

When a qualified identifier is intended to indicate a type that is not a member of the current instance (14.6.2.1) and its inested-name-specifier refers to a dependent type, it must be a prefix with the typename keyword, forming a type-name-specifier.

The qualified identifier in this case is B<decltype(throw (int*)n)>::Type (and the delete version, for which the analysis is exactly the same). Therefore, typename is required if the sub-name specifier or B<decltype(throw (int*)n)>:: is of the dependent type.

Β§14.6.2.1 [temp.dep.type] / p8 says that with six unrelated bullets it’s omitted that

Type depends if it

[...]

(8.7) is the identifier of a simple template in which either the template name is a template parameter or any of the template arguments is a type or expression dependent on the type or depending on the cost, or

(8.8) - denoted by the expression decltype( ) , where the expression depends on the type (14.6.2.2).

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

The expressions of the following forms never depend on the type (because the type of the expression cannot be dependent):

[...]

:: opt delete cast-expression

[...]

throw assignment expression opt

[...]

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

+7
source share

All Articles