This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Why are redundant scope qualifications supported by the compiler, and is it legal?
I wouldn't expect this to compile but it does. Could this be a compiler bug, or does it have some correct meaning?
$ g++ -c scopes.cpp
$ cat scopes.cpp
class Log {
public:
Log() { }
static void fn() { }
};
void test() {
Log::Log::Log::Log::Log::Log::fn();
}
$ g++ --version
g++ (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3
Yes, it's legal. A class's name is inserted into its own namespace, which is called the injected-class-name. From C++03 §9/2:
[...] The class-name is also inserted into the scope of the class itself; this is known as the injected-class-name.
For purposes of access checking, the injected-class-name is treated as if it were a public member name.
Note that Log::Log names the class constructor, which is only allowed in certain contexts, but as long as you end the chain of Log::Log::... with something other than Log (such as fn), then it doesn't name the constructor. Specifically, §3.4.3.1/1a says:
If the nested-name-specifier nominates a class C, and the name specified after the nested-name-specifier,
when looked up in C, is the injected-class-name of C (clause 9), the name is instead considered to name the
constructor of class C. Such a constructor name shall be used only in the declarator-id of a constructor definition
that appears outside of the class definition.
Related
Consider the following code:
#include <iostream>
struct a {
virtual void f() = 0;
};
struct b: a {
private:
void f() override {
std::cout << "b::f()\n";
}
};
struct c: b {
public:
using a::f;
};
int main() {
::c c;
c.f();
}
This compiles and works as expected with g++, clang and msvc, i.e., print b::f().
However, if I replace a and b by this:
struct a {
void f() { }
};
struct b: a {
private:
using a::f;
};
...the code does not compile anymore with gcc but compiles fines with both clang, clang-cl and msvc (thanks StoryTeller and Adrian Mole). I get the following error (on the using a::f; line in c):
error: 'void a::f()' is inaccessible within this context
I cannot find a clear point in the standard about the behavior of using a::f; (in c) in these cases, so are the above well-defined by the standard?
Note: I am not talking about bringing something in the public scope of a class (like using b::f in c if b::f was protected), but really making members from the top-level class accessible in the most-derived class after these have been made inaccessible in an intermediate base class.
I believe GCC is wrong to reject the modified code.
[namespace.udecl]
1 Each using-declarator in a using-declaration introduces a set
of declarations into the declarative region in which the
using-declaration appears. The set of declarations introduced by the
using-declarator is found by performing qualified name lookup
([basic.lookup.qual], [class.member.lookup]) for the name in the
using-declarator, excluding functions that are hidden as described
below.
3 In a using-declaration used as a member-declaration, each
using-declarator's nested-name-specifier shall name a base class of
the class being defined. If a using-declarator names a constructor,
its nested-name-specifier shall name a direct base class of the class
being defined.
So first I'd note that paragraph 3 makes a distinction between a base and a direct base. Therefore we can name a::f in a using declaration. Secondly, according to paragraph 1, name lookup proceeds as one would expect
[class.qual]
1 If the nested-name-specifier of a qualified-id nominates a
class, the name specified after the nested-name-specifier is looked up
in the scope of the class ([class.member.lookup]), except for the
cases listed below. The name shall represent one or more members of
that class or of one of its base classes (Clause [class.derived]).
[class.member.lookup]
1 Member name lookup determines the meaning of a name
(id-expression) in a class scope. Name lookup can result in an
ambiguity, in which case the program is ill-formed. For an
id-expression, name lookup begins in the class scope of this; for a
qualified-id, name lookup begins in the scope of the
nested-name-specifier. Name lookup takes place before access control.
So a::f is to be looked up only in the scope of a or its own base classes. It should not be looked up at all in b. I would argue that therefore the accessibility of the name f in b should not affect the accessibility of the name f in a when doing qualified name lookup.
In a, f is public. And so can be named by a qualified-id in any declarative region where a may be named. That includes c. And so the using declaration uses a valid name for a valid member of a base class. That is accessible in that declarative region. It is therefore valid.
As another data point, GCC has no problem with the accessibility of a::f in other uses. For example, GCC allows forming a pointer to member to a::f inside the scope of c.
struct c: b {
public:
c() {
[[maybe_unused]] auto f = &a::f;
};
};
So it clearly does not consider the name a::f inaccessible in all contexts due to b.
This question already has an answer here:
Why are redundant scope qualifications supported by the compiler, and is it legal?
(1 answer)
Closed 6 years ago.
I noticed by accident that this code compiles and works correctly:
struct M { int some_int; };
static_assert(std::is_same<
decltype(M::M::M::M::some_int) /* <- this */,
int>::value, "Types must be int");
Why is this correct (decltype(M::M::M::M::some_int) <=> decltype(M::some_int))?
What other constructs one can use this pattern with class::class::...::member?
Compiler: Microsoft (R) C/C++ Optimizing Compiler Version 19.00.23824.1 for x86
This is valid in all contexts, not just decltype. A class contains its own name as the injected class name. So within a class A::B::M, the name M is injected to refer to the class A::B::M. This means that you can then use M::M::M::some_member to refer to members of that class, if you really want to.
[Live example]
Note that when referring just to the class name itself (e.g. M::M::M), the situation is slightly different. If such a reference occurs in a place where a reference to a function could also potentially be correct, the syntax is taken to refer to the constructor instead. However, in type-only contexts, even such reference is valid. Example:
M::M::M m; // illegal, M::M interpreted as reference to constructor
struct D : public M::M::M // legal, a function could not be references here, so M::M::M means M
{};
This works because of the injected-class-name:
(N3337) [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.
For purposes of access checking, the injected-class-name is treated as if it were a public member name. [...]
So you can arbitrarily nest these, and they'll work with derived types as well:
struct A { using type = int; };
struct B : public A {};
using foo = B::B::B::A::A::A::type;
Note that in the case of A[::A]*::A, the injected-class-name can be considered to name the constructor instead:
[class.qual]/2: In a lookup in which the constructor is an acceptable lookup result 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
— [...]
the name is instead considered to name the constructor of class C.
Consider this code:
struct foo{};
int main() {
foo::foo a;
}
I would expect this to be well-formed, declaring a variable of type foo by the rule in [class]/2 (N4140, emphasis mine):
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.
For purposes of access checking, the injected-class-name is treated as if it were a public member name.
clang 3.6.0 agrees with me, compiling the above code with no applicable warnings with -Wall -pedantic.
gcc 5.2.0 disagrees, providing the following error message:
main.cpp: In function 'int main()':
main.cpp:5:5: error: 'foo::foo' names the constructor, not the type
foo::foo a;
The above holds no matter how deep the nesting of injected class names, e.g. foo::foo::foo::foo.
Is there a rule which forces that construct to be interpreted as a constructor in that context, or is this agcc bug? Or am I interpreting the standards quote incorrectly?
It appears that clang is wrong in this case. The relevant exception I was looking for is in [class.qual]/2:
2 In a lookup in which function names are not ignored and the nested-name-specifier nominates a class C:
(2.1)
if the name specified after the nested-name-specifier, when looked up in C, is the injected-class-name of C, or
[...]
the name is instead considered to name the constructor of class C.
The standard has a near-equivalent (non-normative, obviously) example:
struct A { A(); };
struct B: public A { B(); };
A::A() { }
B::B() { }
B::A ba;// object of type A
A::A a;// error, A::A is not a type name
struct A::A a2;// object of type A
However, clang actually issues a correct diagnostic in this case:
error: qualified reference to 'A' is a constructor name rather than a type wherever a constructor can be declared
Perhaps clang interprets the line In a lookup in which function names are not ignored as In a lookup in which a constructor declaration is valid, but that doesn't seem to be a correct interpretation.
There is an existing bug for this in the clang bugzilla.
Relevant, but not an answer: The GCC people discussed exactly this for years and figured that it should not be accepted. They explicitly made this an error in GCC 4.5 and newer - in 4.4.7 it was accepted.
BTW: You probably want to use Clang's -Weverything instead of -Wall -pedantic when investigating such stuff.
I think this is the subject of language defect #147
which contains this example
class B { };
class A: public B {
A::B ab; // B is the inherited injected B
A::A aa; // Error: A::A is the constructor
};
At least gcc seems to believe that. :-)
I hope the title actually describes what I wanted to ask...
I wrote a piece of code that compiles with gcc and works as I intended. However, it does not compile with llvm and the code executes differently when compiled with icc!
Here is an example of the problem:
#include <iostream>
using std::cout; using std::endl;
class A {
public:
virtual void foo() { cout << "A::foo()" << endl; }
};
class B : public A {
public:
typedef A base;
virtual void foo() { cout << "B::foo()" << endl; }
};
int main() {
typedef B base;
base* bp = new B();
bp->base::foo();
}
gcc output: A::foo()
icc output: B::foo()
Could somebody explain what does the standard say about this case?
From C++11, §3.4.5/4:
If the id-expression in a class member access is a qualified-id of the
form
class-name-or-namespace-name::...
the class-name-or-namespace-name
following the . or -> operator is first looked up in the class of the
object expression and the name, if found, is used. Otherwise it is
looked up in the context of the entire postfix-expression.
I don't think it can be clearer. This finds B::base, so the output
should be A::foo().
I think this part of the standard is relevant:
3.4.3.1 Class members [class.qual]
1) If the nested-name-specifier of a qualified-id nominates a class, the name specified after the
nested-namespecifier is looked up in the scope of the class (10.2),
except for the cases listed below. The name shall represent one or
more members of that class or of one of its base classes (Clause 10).
[ Note: A class member can be referred to using a qualified-id at any
point in its potential scope (3.3.7). —end note ] The exceptions to
the name lookup rule above are the following:
— a destructor name is
looked up as specified in 3.4.3;
— a conversion-type-id of a
conversion-function-id is looked up in the same manner as a
conversion-type-id in a class member access (see 3.4.5);
— the names
in a template-argument of a template-id are looked up in the context
in which the entire postfix-expression occurs.
— the lookup for a name
specified in a using-declaration (7.3.3) also finds class or
enumeration names hidden within the same scope (3.3.10).
base:: in this case seems to "nominate" a class, so the look up is done in scope of the class. I don't see how any of the exception cases could apply, so it is the scope of the class, as such base is equivalent to A.
(5.1.1-8 indicates that it is a qualified-id in that case and that 3.4.3.1 applies)
I accidentally happened to find this in one of the source codes I was looking at. So, I'm giving a similar smaller example here.
In the file test.h:
#include<iostream>
class test{
int i;
public:
test(){}
//More functions here
};
In the file test.cpp:
#include "test.h"
int main()
{
test test1;
test::test test2;
test::test::test test3;
return 0;
}
First of all, is there a reason to declare test2 that way? Secondly, this code compiles just fine in g++ version 4.4.3 and lower versions. Is there something in the C++ standard, saying, scope resolution operators are ignored when there is no need to resolve scope?
This code is not valid.
It was a bug in g++ that it accepted the code. See "g++ does not treat injected class name correctly." The bug was resolved as fixed in 2009, so it should be fixed in any recent version of g++.
To clarify the situation, as specified in §9/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. For purposes of access checking, the injected-class-name is treated as if it were a public member name.
However, as specified in §3.4.3.1/1:
If the nested-name-specifier of a qualified-id nominates a class, the name specified after the nested-namespecifier is looked up in the scope of the class (10.2), except for the cases listed below.
[ ... §3.4.3.1/2]:
In a lookup in which the constructor is an acceptable lookup result 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) [ ... ] the name is instead considered to name the constructor of class C.
[ ... example: ]
struct A { A(); };
[ ... ]
A::A a; // error, A::A is not a type name
struct A::A a2; // object of type A