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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With