I'm trying to understand what does the following quote mean (3.4.3/3 N3797):
names following the qualified-id are looked up in the scope of the
member’s class or namespace.
namespace A
{
class C
{
public:
static const int a=7;
static int b;
};
}
int A::C::b=a; //7
The scope of the static int b; consist only of the declarative region following by the b's point of declaration. Actually:
The potential scope of a name declared in a class consists not only of
the declarative region following the name’s point of declaration, but
also of all function bodies, default arguments,
exception-specifications, and brace-or-equal-initializers of
non-static data members in that class
This implies that static const int a=7; does not belong to the scope of static int b;. Hence the static const int a=7 cannot be found in the int A::C::b=a;.
It is a typo in the Standard or it is my misunderstanding?
This implies that static const int a=7; does not belong to the scope
of static int b;. Hence the static const int a=7 cannot be found in
the int A::C::b=a;.
No. It implies exactly what you can read there: The potential scope of a name declared in a class also contains function bodies etc. of non-static data members. That does not conflict with the quote above that - the declarative region (and the scope) of a static data member still contains the scope of the class it was declared in itself.
You quoted the relevant part yourself:
names following the qualified-id are looked up in the scope of the
member’s class or namespace
Therefore, since in this code-snippet
int A::C::b=a;
a is used after the declarator-id, it is looked up in the class and found.
The quote says "the scope of the member's class", not "the scope of the member"; so a is looked up in the class scope of C. It can be found there whether or not it's declared after b.
The rule you quoted (item 1 of 3.3.7p1) is intended for names used within a class:
namespace A
{
class C
{
public:
int b = a;
static const int a=7;
};
}
The rule that permits the code you listed is item 5 of that same paragraph:
5) The potential scope of a declaration that extends to or past the end of a class definition also extends to the regions defined by its member definitions, even if the members are defined lexically outside the class [...]
There is clearly some overlap between the applicability items 1 and 5 of 3.3.7p1, but that doesn't matter, as they have the same effect where they do overlap.
3.4.3p3 is saying the same as 3.3.7p1 item 5, just with different wording; it clarifies that the type appearing before the qualified-id is not part of the "region defined by [the] member definition".
Related
Sample code:
struct X { void f() {} };
typedef X A;
namespace N {
struct A { void g() {} };
};
using N::A;
int main()
{
A a;
a.f();
}
This code compiles correctly, and A a; creates an X, not a N::A.
What rule in the standard covers this behaviour? I was expecting an error that A a; would be ambiguous. If I name the first struct A instead of X and remove the typedef, then I do get such an error. (g++ 8.3)
According to [namespace.udecl]/1 using N::A introduces the unqualified A into the declarative region in which the using declaration appears.
... the unqualified-id is declared in the declarative region in which the using-declaration appears as a synonym for each declaration introduced by the using-declarator.
So the ambiguity is covered jointly by [namespace.udecl]/13
Since a using-declaration is a declaration, the restrictions on declarations of the same name in the same declarative region also apply to using-declarations.
and [basic.scope.declarative]/4
Given a set of declarations in a single declarative region, each of
which specifies the same unqualified name,
they shall all refer to the same entity, or all refer to functions and function templates; or
exactly one declaration shall declare a class name or enumeration name that is not a typedef name and the other declarations shall all refer to the same variable, non-static data member, or enumerator, or all refer to functions and function templates; in this case the class name or enumeration name is hidden.
The typedef declaration and the using declaration in the OP don't fill either bullet, so the pair of declarations in the same declarative region is ill-formed.
Is this ill-formed or well-formed according to the c++ standard?
namespace M { struct i {}; }
namespace N { static int i = 1; }
using M::i;
using N::i;
int main() { sizeof (i); }
Clang rejects it and GCC accepts it.
According to [namespace.udir-6] (http://eel.is/c++draft/basic.namespace#namespace.udir-6):
If name lookup finds a declaration for a name in two different
namespaces, and the declarations do not declare the same entity and do
not declare functions, the use of the name is ill-formed.
How should we interpret this? Remember that each using-declaration are declaring a name by [namespace.udecl]p1 (http://eel.is/c++draft/namespace.udecl#1):
A using-declaration introduces a name into the declarative region in
which the using-declaration appears.
using-declaration:
using typenameopt nested-name-specifier unqualified-id ;
The member name specified in a using-declaration is declared in the
declarative region in which the using-declaration appears. [ Note:
Only the specified name is so declared; specifying an enumeration name
in a using-declaration does not declare its enumerators in the
using-declaration's declarative region. — end note ] If a
using-declaration names a constructor ([class.qual]), it implicitly
declares a set of constructors in the class in which the
using-declaration appears ([class.inhctor]); otherwise the name
specified in a using-declaration is a synonym for a set of
declarations in another namespace or class.
So we have 4 declarations of the name i.
Which of these does unqualified name lookup of i in sizeof(i) find?
Does it only find using M::i; and using N::i; which are both in the same namespace (the global namespace) so the program is well-formed?
Or does it only find struct i {}; and static int i = 1; which are in different namespaces so the program is ill-formed?
Or do we have some other alternative?
bogdan already has to the answer, but to build on why your intuition is incorrect, you cited:
If name lookup finds a declaration for a name in two different namespaces, and the declarations do not declare the same entity and do not declare functions, the use of the name is ill-formed.
But in the example, we have:
namespace M {
struct i {}; // declares M::i, entity class type
}
namespace N {
static int i = 1; // declares N::i, entity variable
}
using M::i; // declares ::i, synonym of M::i
using N::i; // declares ::i, synonym of N::i
// hides (*) the other ::i
int main() {
sizeof (i);
}
To elaborate on (*), we have two declaration of i in the global namespace ::. From [basic.scope.hiding]:
A class name (9.1) or enumeration name (7.2) can be hidden by the name of a variable, data member,
function, or enumerator declared in the same scope. If a class or enumeration name and a variable, data
member, function, or enumerator are declared in the same scope (in any order) with the same name, the
class or enumeration name is hidden wherever the variable, data member, function, or enumerator name is
visible.
So with the two is in the same scope, the class is hidden (irrespective of the ordering of the using-declarations!), and sizeof(i) refers to the ::i that is the synonym of N::i. Both is were in the same namespace (::), which is why your quote doesn't apply. This differs from your earlier question, where you had using-directives instead:
using namespace M;
using namespace N;
There i would be found in two different namespaces, referring to two different non-function entities. Hence, the error. Here, Clang is wrong and GCC is correct.
N4527 [7.3.3p13]:
Since a using-declaration is a declaration, the restrictions on
declarations of the same name in the same declarative region (3.3)
also apply to using-declarations. [ Example:
namespace A {
int x;
}
namespace B {
int i;
struct g { };
struct x { };
void f(int);
void f(double);
void g(char); // OK: hides struct g
}
void func() {
int i;
using B::i; // error: i declared twice
void f(char);
using B::f; // OK: each f is a function
f(3.5); // calls B::f(double)
using B::g;
g(’a’); // calls B::g(char)
struct g g1; // g1 has class type B::g
using B::x;
using A::x; // OK: hides struct B::x
x = 99; // assigns to A::x
struct x x1; // x1 has class type B::x
}
—end example ]
Note the using-declarations for the two different xs - it's the same case as your example.
Your first quote is referring to using-directives, not using-declarations.
Unqualified name lookup for the i in sizeof(i) finds the is in the global namespace. Since they are declarations in the same scope, according to [3.3.10p2] (quote below), the variable i hides the struct.
A class name (9.1) or enumeration name (7.2) can be hidden by the name
of a variable, data member, function, or enumerator declared in the
same scope. If a class or enumeration name and a variable, data
member, function, or enumerator are declared in the same scope (in any
order) with the same name, the class or enumeration name is hidden
wherever the variable, data member, function, or enumerator name is
visible.
So, the code is well-formed and Clang is wrong to reject it.
MSVC (12 and 14) accepts the example.
Basically, think of the name introduced by a using-declaration as just another name for some entity, which is also named somewhere else (the place designated by the nested-name-specifier of the qualified-id in the using-declaration). This is different from what a using-directive does; I tend to think of using-directives as "name lookup tweaks".
I have the following simplified code
namespace Namespace
{
int foo() { return 1; }
class Class
{
public:
int foo() const { return 2; }
class Nested {
public:
Nested()
{
cout << foo() << endl;
}
};
};
}
And I got this error:
error: cannot call member function ‘int Namespace::Class::foo() const’
without object:
cout << foo() << endl;
^^^^^
It seems that compiler selects non static int Namespace::Class::foo() const instead of global function int Namespace::foo().
But how can it be expected that non-static function from other class can be called without object? Nested object has no access to surrounding Class object - this is not Java after all.
I read carefully through overload resolution from cppreference I cannot find the rationale for this behavior. I rather doubt that this is gcc error.
Can you point the language rules responsible for this behavior?
And how do you deal with such problems?
[UPDATE]
Just an answer for 2nd question. Workaround is simple, there is a need to tell compiler that such global function exists:
Nested()
{
using Namespace::foo; //< workaround: inform compiler such function exists
cout << foo() << endl;
}
BTW, is that workaround correct? Are there any better solutions?
I read carefully through overload resolution from cppreference I cannot find the rationale for this behavior. Can you point the language rules responsible for this behavior?
Before the overload resolution procedure selects the best viable function, an initial set of candidates is generated during the name lookup phase. In other words, the expected behavior should be searched for in the Name lookup section, not in the Overload resolution one.
The name lookup procedure for an unqualified name is described in the C++ standard:
§3.4.1 [basic.lookup.unqual]/p8:
A name used in the definition of a member function (9.3) of class X following the function’s declarator-id or in the brace-or-equal-initializer of a non-static data member (9.2) of class X shall be declared in one of the following ways:
— before its use in the block in which it is used or in an enclosing block (6.3), or
— shall be a member of class X or be a member of a base class of X (10.2), or
— if X is a nested class of class Y (9.7), shall be a member of Y, or shall be a member of a base class of Y
(this lookup applies in turn to Y’s enclosing classes, starting with the innermost enclosing class), or [...]
and only if still not found:
— if X is a member of namespace N, or is a nested class of a class that is a member of N, or is a local class or a nested class within a local class of a function that is a member of N, before the use of the name, in namespace N or in one of N's enclosing namespaces.
Since the name lookup ends as soon as the name is found (§3.4.1 [basic.lookup.unqual]/p1):
In all the cases listed in 3.4.1, the scopes are searched for a declaration in the order listed in each of the respective categories; name lookup ends as soon as a declaration is found for the name.
in your case no other scopes are searched as soon as int foo() const { return 2; } is encountered.
Workaround is simple, there is a need to tell compiler that such global function exists:
using Namespace::foo; //< workaround: inform compiler such function exists
Is that workaround correct?
§7.3.3 [namespace.udecl]/p1:
A using-declaration introduces a name into the declarative region in which the using-declaration appears.
§3.3.1 [basic.scope.declarative]/p1:
Every name is introduced in some portion of program text called a declarative region, which is the largest part of the program in which that name is valid, that is, in which that name may be used as an unqualified name to refer to the same entity.
Introducing a name with a using-declaration impacts the unqualified name lookup in a way such that it finds that function in its first step, namely that name becomes declared:
— before its use in the block in which it is used or in an enclosing block (6.3)
Are there any better solutions?
One can use a qualified name when referring to a function from some namespace scope, explicitly indicating what symbol is being referred to:
Nested()
{
cout << Namespace::foo() << endl;
}
This was indeed about Name Lookup and not about Overload Resolution.
Indeed, as the standard stipulates, §3.4.1/1 of N3376 (emphasis mine):
In all the cases listed in 3.4.1, the scopes are searched for a declaration in the order listed in each of the respective categories; name lookup ends as soon as a declaration is found for the name. If no declaration is found, the program is ill-formed.
So in fact, the lookup stops as soon as a declaration is found.
Then, you can find more about the case you're dealing with in §3.4.1/8 which deals with names used in class inside a member function definition, especially this part:
if X is a nested class of class Y (9.7), shall be a member of Y, or shall be a member of a base class of Y (this lookup applies in turn to Y’s enclosing classes, starting with the innermost enclosing class)
And if not found, in enclosing namespaces.
In your case, that means that Namespace::Class::foo() is the first name found during the lookup, and it stops as soon as it has found a name, so Namespace::foo() isn't even considered.
The example given by the standard illustrates the lookup path:
class B { };
namespace M {
namespace N {
class X : public B {
void f();
};
}
}
void M::N::X::f() {
i = 16;
}
The following scopes are searched for a declaration of i:
1) outermost block scope of M::N::X::f, before the use of i // 2) scope of class M::N::X
3) scope of M::N::X’s base class B
4) scope of namespace M::N
5) scope of namespace M
6) global scope, before the definition of M::N::X::f
And as to how you deal with such problem, you have to qualify the call, i.e.
cout << Namespace::foo() << endl;
so that the compiler selects the function you want through qualified lookup.
Piotr S. and JBL explained the why of your problem.
IMHO the simpler solution is to use the qualified name of the function :
public:
Nested()
{
cout << Namespace::foo() << endl;
}
Consider the following code snippet:
class A
{
int b[A::a]; //1, error
void foo(){ int b = A::a; } //2, ok
static const int a = 5;
}
Clause 3.4.3.1/1 (Qualified name lookup, class members) said:
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 (10.2)
This implies that the name a after the nested-name-specifier both in //1 and in //2 will be looked up in the class scope.
Clause 10.2 (Member name lookup) said:
10.2/2
The following steps define the result of name lookup for a member name
f in a class scope C.
10.2/3
The lookup set for f in C, called S(f, C)...
S(f, C) is calculated as follows:
10.2/4
If C contains a declaration of the name f, the declaration set
contains every declaration of f declared in C that satisfies the
requirements of the language construct in which the lookup occurs.
The following is unclear for me:
From the quotes I cited implies that for both //1 and //2 the same member lookup rules shall be applied. But actually its a different. Why is my reasoning wrong?
Note: I know about unqualified name lookup rules into the class scope. And I understood that behavior in the following code snippet:
class A
{
int b[a]; //error
void foo(){ int b = a; } //ok
static const int a = 5;
}
It is because that behavior described in the sections 3.4.1/7 and 3.4.1/8 (Unqualified name lookup).
The error is because when int b[A::a]; is being processed, A does not yet have a symbol a. At that point of compilation, A is still incomplete because we have not reached the closing } of the class definition yet. The compiler doesn't "look ahead" to see if future lines of source code contain a definition of a.
You can see this by reversing the order of the lines:
class A
{
static const int a = 5;
int b[A::a]; // OK
};
The function definition does not have the same problem because inline function bodies are not compiled until after compilation of the class definition. (Sorry, I don't have standard references handy for this)
The member declaration int b[A::a]; is not in the potential scope of A::a (3.3.7p1), while the body of void A::foo() is in the potential scope (3.3.7p1b1):
1) The potential scope of a name declared in a class consists not only of the declarative region following the name's point of declaration, but also of all function bodies, brace-or-equal-initializers of non-static data members, and default arguments in that class (including such things in nested classes).
3.4.3.1p1 references the potential scope rule in a note:
[...] [ Note: A class member can be referred to using a qualified-id at any point in its potential scope (3.3.7). — end note ] [...]
Of course, notes are non-normative, so the conclusion must be that the potential scope rule is implied by other material in the standard. I believe this other material is specifically 3.3.2p5:
After the point of declaration of a class member, the member name can be looked up in the scope of its class. [...]
By implication, prior to the point of declaration of that class member, that member name cannot be looked up in the scope of that class.
This is a follow-up question.
In the previous question, #JohannesSchaub-litb said that the following code is not fully standard-conformant:
class { int i; }; //unnamed-class definition. § 9/1 allows this!
and then he added,
while it is grammatically valid, it breaks the rule that such a class must declare at least one name into its enclosing scope.
I couldn't really understand this. What name is he talking about?
Could anyone elaborate on this further (preferably quoting the Standard)?
Clause 9 of the standard allows class {public: int i;} (note the lack of a final semicolon) because this decl-specifier-seq for an unnamed class might be used in some other construct such as a typedef or a variable declaration. The problem with class {public: int i;}; (note that the final semicolon is now present) is that this class specification now becomes a declaration. This is an illegal declaration per clause 7, paragraph 3 of the standard:
In such cases, and except for the declaration of an unnamed bit-field (9.6), the decl-specifier-seq shall introduce one or more names into the program, or shall redeclare a name introduced by a previous declaration.
The point is that by declaring class{ int i; }; you are assembling a bunch of symbol (int i, in this case) you will not be able to use anywhere else in whatever code.
For this code to make sense you should at least do one of the following:
class Myclass { int i; }; //I can furthermore instantiate variables of Myclass
class { int i; } myvar; //This in fact creates a myvar object
typedef class { int i; } MyType; //I can funthermore instantiate variables of MyType
By saying just class{ int i; }; you are saying to the compiler:
keep an int and name it i,
wrap it into a class I'll never call and...
forget it! (};)
If you remove that declaration from your program, nothing will change.
class { int i; }; is not a valid declaration because it is a simple-declaration without an init-declarator-list but it doesn't introduce (or re-declare) a class name.
ISO/IEC 14882:2011 7 [dcl.dcl] / 3:
In a simple-declaration, the optional init-declarator-list can be omitted only when declaring a class (Clause 9) or enumeration (7.2), that is, when the decl-specifier-seq contains either a class-specifier, an elaboratedtype-specifier with a class-key (9.1), or an enum-specifier. In these cases and whenever a class-specifier or enum-specifier is present in the decl-specifier-seq, the identifiers in these specifiers are among the names being declared by the declaration (as class-names, enum-names, or enumerators, depending on the syntax). In such cases, and except for the declaration of an unnamed bit-field (9.6), the decl-specifier-seq shall introduce one or more names into the program, or shall redeclare a name introduced by a previous declaration.
The error message from GCC explains it quite succinctly:
$ cat > a.cc
class { int i; };
$ g++ -Wall -std=c++98 a.cc
a.cc:1: error: abstract declarator ‘<anonymous class>’ used as declaration
class { int i; } is an abstract-declarator (Standard, §8) but not a valid declaration (§7). That's the rule that #JohannesSchaub-litb referenced: for a valid declaration, you need something to be declared, e.g. a class name or variable name.
You are breaking the the [basic.scope.pdecl]/6, which says :
The point of declaration of a class first declared in an elaborated-type-specifier is as follows:
— for a declaration of the form
class-key attribute-specifier-seqopt identifier ;
the identifier is declared to be a class-name in the scope that contains the declaration, otherwise
— for an elaborated-type-specifier of the form
class-key identifier
if the elaborated-type-specifier is used in the decl-specifier-seq or parameter-declaration-clause of a
function defined in namespace scope, the identifier is declared as a class-name in the namespace that
contains the declaration; otherwise, except as a friend declaration, the identifier is declared in the
smallest namespace or block scope that contains the declaration. [ Note: These rules also apply within
templates. — end note ] [ Note: Other forms of elaborated-type-specifier do not declare a new name,
and therefore must refer to an existing type-name. See 3.4.4 and 7.1.6.3. — end note ]
you are not creating a variable of an anonymous type
you are not create a type
There is another example (in [basic.def]/2) from the standard that proves your example is not standard compliant :
struct S { int a; int b; }; // defines S, S::a, and S::b
struct X { // defines X
int x; // defines non-static data member x
static int y; // declares static data member y
X(): x(0) { } // defines a constructor of X
};
int X::y = 1; // defines X::y
enum { up, down }; // defines up and down
namespace N { int d; } // defines N and N::d
namespace N1 = N; // defines N1
X anX; // defines anX
Your example doesn't define anything (except an anonymous struct, who's fields can not be accessed).
Note an exception about the enum, because this case introduces two values to use.