Why Is There an Injected Class Name

Why is there an injected class name?

The injected class name means that X is declared as a member of X, so that name lookup inside X always finds the current class, not another X that might be declared at the same enclosing scope, e.g.

void X() { }
class X {
public:
static X create() { return X(); }
};

Is the create() function creating a temporary X object or calling the function X? At namespace scope it would call the function, so the purpose of the injected-class-name is to ensure that within the body of X the name always finds the class itself (because name lookup starts in the class' own scope before looking in the enclosing scope).

It's also helpful inside class templates, where the injected class name can be used without a template argument list, e.g. using simply Foo instead of the full template-id Foo<blah, blah, blah>, so it's easy to refer to the current instantiation. See DR 176 for a change between C++98 and C++03 that clarified that.

The idea of the injected class name was present in C++98, but the terminology was new for C++03.

C++98 says:

A class-name is inserted into the scope in which it is declared immediately after the class-name is seen. The class-name is also inserted into the scope of the class itself.

The second sentence was changed by DR 147 so C++03 says in [class]/2:

A class-name is inserted into the scope in which it is declared immediately after the class-name is seen. The class-name is also inserted into the scope of the class itself; this is known as the injected-class-name.

Even before C++98, the ARM has roughly equivalent wording that means the class' name can always be used in the class body to refer to the class itself:

The name of a class can be used as a class-name even within the member-list of the class specifier itself.

  • For example,

    class link { link* next; };

What happens when Injected-Class-Name occurs? (C++)

So what is really happening in the code? Is X* p turned into X::X* p?

Basically. The name lookup rules start in the narrowest scope. When you do X* p; in f is looks in f's scope and doesn't find anything. Then it checks X's scope since f is scoped to X. It finds X since it is injected into the class scope so it stops there and you get the class type.

When you do ::X* q; then ::X says look for X in the global namespace, and there is finds a variable, not a type so you get an error.

Injected-class-names of class templates

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 <>.

Why injected-class-name is sometimes not treated as a template name in a class template?

The relevant rule here is [temp.local]/4:

A lookup that finds an injected-class-name ([class.member.lookup]) can result in an ambiguity in certain cases (for example, if it is found in more than one base class). If all of the injected-class-names that are found refer to specializations of the same class template, and if the name is used as a template-name, the reference refers to the class template itself and not a specialization thereof, and is not ambiguous. [ Example:

template <class T> struct Base { };
template <class T> struct Derived: Base<int>, Base<char> {
typename Derived::Base b; // error: ambiguous
typename Derived::Base<double> d; // OK
};

— end example ]

Which I think means that this is a gcc and a clang bug. In:

using a = A<Base>;

All of the injected-class-names that are found do refer to specializations of the same class template (Base<T>) and the name is used as a template-name (because it's a template argument for a template template parameter), so this should just refer to the class template itself and not be ambiguous.

C++ class name injection

Since the following code compiles and runs correctly in other compilers, it is a bug in MSVC2015.

#include <boost/type_index.hpp>
#include <iostream>

struct inj
{
int g;
};

typedef struct inj::inj inj2;

int main()
{

inj2 ii;
std::cout << boost::typeindex::type_id_with_cvr<decltype(ii)>().pretty_name() << '\n';
}

UPDATE: Reported as bug.

Can an injected class name be used as a type name in a friend declaration?

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 is a template-name that refers to the class template itself.

The condition in bold does not apply to the example because the name appears within a friend class declaration, but not a friend class template declaration.

Similar code where the part in bold does apply:

template <typename T>
class Singleton
{
};

class Logger : public Singleton<Logger> {
template <typename> friend class Singleton;
};

The friend class template declaration redeclares the class template Singleton and makes it a friend. The same syntax is also legal as a first declaration of a class template (see the example in [temp.friend]/1.4 where class template frd is declared and befriended), but a first declaration can't be an instance of an injected class name.

Class Name Injection and Constructors

Clang-5 is very much correct. Over at [class.qual]/2:

In a lookup in which function names are not ignored 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
  • ...

the name is instead considered to name the constructor of class C.

As for the other part of the question. Yes, it's definitely worth it to submit bug reports. Standard compliance (or at least more diagnostics towards it) are to be encouraged IMO.

Is a nested class name be considered as a current instantiation when it is used in a enclosing class template

Is a nested class name be considered as a current instantiation when it is used in a enclosing class template?

No.

However, as with many other passages of the standard, paragraph [temp.dep.type]/1.1 is indeed a mouthful:

A name refers to the current instantiation if it is

  • (1.1) in the definition of a class template, a nested class of a class template, a member of a class template, or a member of a nested class of a class template, the injected-class-name of the class template or nested class, [...]

and we may need to break it down into the different cases to see that it does not apply to a nested-class-name when in the definition of class template where, in turn, the nested class is defined:

A name refers to the current instantiation if it is,

  • in the definition of a class template, the injected-class-name of the class template ["or nested class" does not apply for this case]
  • in the definition of a nested class of a class template, the injected-class-name of the class template or [the] nested class
  • in the definition of a member of a class template, the injected-class-name of the class template ["or nested class" does
    not apply for this case]
  • in the definition of a member of a nested class of a class template, the injected-class-name of the class template or [the] nested class

where I have emphasized the connection of the "if it is in the definition of a X (of a Y), the injected-class-name of the X (or [the] Y)". Noting particularly that when the definition is of a class template (when X is class template), there is no "a nested class" to refer back to at the latter part, when describing the "injected-class-name of the ...".

Conversely, there is no way interpret [temp.dep.type]/1.1 as

" in the definition of a class template, the injected-class-name of the class template or any nested class of the class template "

Thus, in your example, #2, we are in the definition of a class template (namely A), meaning only the name of the class template itself, namely A, refers to the current instantiation.



Do you agree the sentence is vague?

Given the above, I would not say that it is vague (as this would imply some ambiguity, which I cannot see), but possible needlessly complex. It could arguably be clearer if [temp.dep.type]/1.1 was split into two separate paragraphs:

A name refers to the current instantiation if it is

  • (1.1a) in the definition of a class template, or a member of a class template, the injected-class-name of the class template,
  • (1.1b) in the definition of a nested class of a class template, or a member of a nested class of a class template, the injected-class-name of the class template or nested class, [...]


Related Topics



Leave a reply



Submit