问题
Inspired by the code in this answer. Consider:
template<class>
class A { };
int main()
{
A<float> a(A<float>::A<int>());
return 0;
}
Is this code
- ill-formed, because
A<float>::A
names the constructor (per §3.4.3.1 [class.qual]/p2) and cannot be used in this context (plus the<int>
would complete fail to parse anyway), or - well-formed, with
A<float>::A
being the injected-class-name, used as a template-name (§14.6.1 [temp.local]), such thatA<float>::A<int>
means exactly the same asA<int>
, anda
being declared as a function (due to the most vexing parse)?
g++ says 1. clang says 2, and so does ICC 13. Which compiler is correct?
回答1:
gcc
is correct; your snippet is ill-formed!
// reduced testcase
template<class T>
class A { };
int main () {
A<float>::A<int> x; // ill-formed, bug in `clang` and `icc`
}
In the above reduced testcase we have a nested-name-specifier, A<float>::
, followed by an unqualified-id A
, which is then followed by some gibberish (<int>
).
This is because the context in which the nested-name-specifier appears mandates that, during a look-up, function names are included (meaning that the constructor is found first, and the expression is ill-formed).
Relevant Bug Reports:
- llvm.org/bugs/ - #8263; Incorrect constructor name resolution
How to circumvent the "problem"?
There are contexts in which member names that are looked up through a nested-name-specifier (that nominates a class) shall not include functions (hence, contexts where the constructor is not found), below are a few examples:
template<class T>
struct A {
typedef T value_type;
};
struct A<float>::A<int> x; // ok, context: elaborate-type-specifier
typename A<float>::A<int> (); // ok, context: [expr.type.conv]p1
A<float>::A::value_type x; // ok, context: nested-name-specifier
struct X : A<float>::A<int> { }; // ok, context: base-specifier
What does the Standard say?
3.4.3.1p2
Class members[class.qual]
In a lookup in which function names are not ignored88 and the nested-name-specifier nominates a class C:
- if the name specified after the nested-name-specifier, when looked up in C, is the injected-class-name of C (Clause 9), or
- in a using-declaration (7.3.3) that is a member-declaration, if the name specified after the nested-name-specifier is the same as the identifier or the simple-template-id's template-name in the last component of the *nested-name-specicifier,
the name is instead considered to name the constructor of class C.
[ Note: ... ]
Such a constructor name shall be used only in the declarator-id of a declaration that names a constructor or in a using-declaration.
88. Lookups in which function names are ignored include names appearing in a nested-name-specifier, an elaborated-type-specifier, or a base-specifier.
14.6.1p2
Locally declared names[temp.local]
Like normal (non-template) classes, class templates have an injected-class-name (Clause 9). The injected-class-name can be used as a template-name or a type-name.
When it is used with a template-argument-list, as a template-argument for a template template-parameter, or as the final identifier in the elaborated-type-specifier of a friend class template declaration, it refers to the class template itself.
Otherwise, it is equivalent to the template-name followed by the template-parameters of the class template enclosed in
<>
.
来源:https://stackoverflow.com/questions/26558507/injected-class-names-of-class-templates