qualified names in C++ class declaration - c++

According to this page, the class name can be "optionally qualified". Thus, I expect the following code to compile:
struct ::globalSt {};
In MSVC 2013u4, I get an error:
Error 1 error C2039: 'globalSt' : is not a member of '`global namespace''
Am I misinterpreting the reference or is that a MSVC bug?

If you define a class with a qualified name, the name must have been previously declared. [class]/11
If a class-head-name contains a nested-name-specifier, the class-specifier shall refer to a class that was
previously declared directly in the class or namespace to which the nested-name-specifier refers, or in an
element of the inline namespace set (7.3.1) of that namespace (i.e., not merely inherited or introduced by
a using-declaration), and the class-specifier shall appear in a namespace enclosing the previous declaration.
In such cases, the nested-name-specifier of the class-head-name of the definition shall not begin with a
decltype-specifier.

For this to work the class must be already delcared. If you put a struct globalst; somewhere in your code before your definition it will work fine.
For example if you do
struct MyStruct;
then do
struct ::MyStruct {};
it should compile.
Tested with MSVC 2013.

Related

Defining sub-structure of sub-class inside parent class

Consider the following snippet of code:
class MyClass
{
private:
struct PrivateClass
{
struct SubStruct;
};
public:
struct PrivateClass::SubStruct {};
private:
PrivateClass::SubStruct member;
};
MSVC and gcc compile this code without any errors. clang, however, produces the following error:
<source>:10:26: error: non-friend class member 'SubStruct' cannot have a qualified name
struct PrivateClass::SubStruct {};
So, who's right? Is this a clang bug?
According to [class]/11 in the C++ 17 Standard:
If a class-head-name contains a nested-name-specifier, the
class-specifier shall refer to a class that was previously declared
directly in the class or namespace to which the nested-name-specifier
refers, or in an element of the inline namespace set (10.3.1) of that
namespace (i.e., not merely inherited or introduced by a
using-declaration), and the class-specifier shall appear in a
namespace enclosing the previous declaration. In such cases, the
nested-name-specifier of the class-head-name of the definition shall
not begin with a decltype-specifier.
So it seems the clang compiler is right.
Standard (latest draft) says:
[class.nest]
If class X is defined in a namespace scope, a nested class Y may be declared in class X and later defined in the definition of class X or be later defined in a namespace scope enclosing the definition of class X.
In this example, the class SubStruct that is declared in the class PrivateClass is defined in neither later in PrivateClass nor later in a namespace scope (but rather, later in a class scope of the outer MyClass). Nor is PrivateClass itself defined in a namespace scope.
Unless there is another rule allowing this, the definition of the sub-nested class as in the example is not at least explicitly allowed. Clang seems to be correct.

std::is_same for a not yet defined/declared class

Is thw following code, a gcc bug?
Checking if T type is a not defined yet class Circle, returns false.
#include <iostream>
using namespace std;
// uncomment to work
//struct Circle;
struct T_traits
{
template<typename T>
constexpr static id() { return is_same<T, class Circle>(); }
};
struct Circle{};
int main()
{
cout << T_traits::id<Circle>() << "\r\n";
return 0;
}
return is_same<T, class Circle>();
This will actually declare a local class called Circle when you comment out the global declaration. [basic.lookup.elab]/2:
If the elaborated-type-specifier has no nested-name-specifier, and
unless the elaborated-type-specifier appears in a declaration with
the following form: class-key
attribute-specifier-seqopt identifier ; the
identifier is looked up according to 3.4.1 but ignoring any non-type
names that have been declared.[..]
If the elaborated-type-specifier is introduced by the class-key
and this lookup does not find a previously declared type-name
[..] the elaborated-type-specifier is a declaration that
introduces the class-name as described in 3.3.2.
The lookup is simple unqualified name lookup as defined in §3.4.1. Lookup is done in the definition context of T_traits as we are not dealing with dependent stuff, so the declaration of Circle right before main is never considered.
§3.3.2/7 (alias [basic.scope.pdecl]/7):
The point of declaration of a class first declared in an
elaborated-type-specifier is as follows:
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 ]
However, removing the class keyword won't work either - as mentioned before, the identifier isn't dependent and thus looked up in the definition context. If no declaration is found by this lookup, a diagnostic must be issued by the compiler - even if no specialization is instantiated.

Is my interpretation of the word 'first' in §3.3.2/6 correct?

In the snippet below I can understand (from §3.3.2/6 second bullet point) that the name B in the declaration struct B* p; is injected into the global namespace as a class-name.
struct A {
// struct B{};
int B;
struct B* p;
};
void f(B&) {}
int main()
{
A a;
f(*a.p);
}
§3.3.2/6:
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-seq opt 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 ]
However if I uncomment the definition of struct B{}; inside struct A, what I said earlier with regard to the injection of name B into the global namespace, doesn't occur anymore, as the code doesn't compile. I believe this has to do with the word first (emphasis mine) above, since now the class-name B, in the declaration struct B* p; is no more its first declaration in its declarative region. Am I correct saying this?
Assuming my interpretation is correct, why is it that the class-name B is not injected in the global namespace in this case? Note that the nested class struct B{}; will be hidden inside A in this case, i.e., even if we change the declaration of function f to void f(A::B&) the code won't compile.
There is still one other point that isn't clear to me: what made the implementers to decide for the class-name injection into the namespace, or block scope, containing the elaborated-type-specifier, in the second bullet point above? That is, why didn't they leave the class-name declaration inside the class scope?
You're correct, that first keyword in §3.3.2/6 is also the reason for the following:
struct A {
struct B *p;
struct B{};
int b;
};
void f(B* arg) {
std::cout << std::is_same<decltype(arg), A::B*>::value; // not the same type
}
int main()
{
A a;
f(a.p);
}
why is it that the class-name B is not injected in the global namespace in this case?
As dyp pointed out, [basic.lookup.elab]/2 explains that 3.3.2 is only carried out in case no previous declaration could be found
If the elaborated-type-specifier is introduced by the class-key and
this lookup does not find a previously declared type-name, or if the
elaborated-type-specifier appears in a declaration with the form:
class-key attribute-specifier-seqopt identifier ;
elaborated-type-specifier is a declaration that introduces the
class-name as described in 3.3.2.the
Finally I tracked down this behavior to possibly be an inheritance from C99 6.7.2.3/p8
If a type specifier of the form
struct-or-union identifier
occurs
other than as part of one of the above forms, and no other declaration
of the identifier as a tag is visible, then it declares an incomplete
structure or union type, and declares the identifier as the tag of
that type.113)
113) A similar construction with enum does not exist.
I would say your interpretation is correct. When you uncomment struct B{};, the struct B* p; line will simply refer to that struct A::B.
To answer your question why, when you leave struct B {}; commented out, the name struct B is inserted into the global scope. I would say that's because the authors didn't want you to (somewhat) silently declare B as a member of A without using a member-specification for the name B.

VS 2013 - IntelliSense reporting false positives?

I'm working on a C++ project where, among other things, I have an interface with a few pure virtual methods. The problem arises when I try to implement that interface - IntelliSense doesn't seem to agree with the derived class's method declaration. An example of such a method:
// DLL_EXPORT -> #define DLL_EXPORT __declspec(dllexport)
// IPlayer
DLL_EXPORT virtual const Grid& GetGrid() const = 0;
Declaration in one of the derived classes:
// Human : IPlayer
DLL_EXPORT const Grid& IPlayer::GetGrid() const;
The error it keeps nagging me with - "IntelliSense: declaration must correspond to a pure virtual member function in the indicated base class". The code compiles without errors and runs fine, all of the "problematic" methods do their jobs as expected during run time. What is worth mentioning is that the error disappears if I remove the IPlayer:: scope qualifier in the derived class. I wanted to keep it there for readability reasons. Also, I am NOT proficient in C++ so there could be something obviously wrong with the example I've provided.
Minimized example:
struct C { virtual void f() = 0; };
struct D : C { void C::f() { } };
This doesn't compile in any version of g++ or clang that I tested. Intellisense in VS2013 uses the EDG frontend, and to quote Jonathan Wakely, "If GCC, Clang and EDG all agree and MSVC disagrees that usually means MSVC is wrong."
To make things more interesting, the relevant paragraphs in the standard actually changed between C++11 and C++14.
In C++11, this is flat-out illegal (N3337 §8.3 [dcl.meaning]/p1):
A declarator-id shall not be qualified except for the definition of a
member function (9.3) or static data member (9.4) outside of its
class, the definition or explicit instantiation of a function or
variable member of a namespace outside of its namespace, or the
definition of an explicit specialization outside of its namespace, or
the declaration of a friend function that is a member of another class
or namespace (11.3).
This sentence was removed in C++14 as a result of CWG issue 482. The proposed resolution for that issue has the following note:
[Drafting note: The omission of “outside of its class” here does not
give permission for redeclaration of class members; that is still
prohibited by 9.2 [class.mem] paragraph 1. The removal of the
enumeration of the kinds of declarations in which a qualified-id can
appear does allow a typedef declaration to use a qualified-id, which
was not permitted before; if that is undesirable, the prohibition can
be reinstated here.]
In C++14, the only applicable rule in §8.3 [dcl.meaning]/p1 is now (quoting N3936):
When the declarator-id is qualified, the declaration shall refer to a
previously declared member of the class or namespace to which the
qualifier refers (or, in the case of a namespace, of an element of the
inline namespace set of that namespace (7.3.1)) or to a specialization
thereof; the member shall not merely have been introduced by a
using-declaration in the scope of the class or namespace nominated by
the nested-name-specifier of the declarator-id.
The relevant part of §9.2 [class.mem]/p1 is:
Except when used to declare friends (11.3) or to introduce the name of
a member of a base class into a derived class (7.3.3),
member-declarations declare members of the class, and each such
member-declaration shall declare at least one member name of the
class. A member shall not be declared twice in the
member-specification, except that a nested class or member class
template can be declared and then later defined, and except that an
enumeration can be introduced with an opaque-enum-declaration and
later redeclared with an enum-specifier.
Since a using-declaration "to introduce a member of a base class into a derived class" is made an explicit exception, it appears that base class members are not considered members for the purposes of the rule that "member-declarations declare members of the class, and each such member-declaration shall declare at least one member name of the class". If so, then it follows that using a qualified-id like void C::f() { } in a member-declaration is also not allowed in C++14, since that qualified-id refers to a member of C, not a member of D.

Namespace member definition

namespace M{
void f();
void M::f(){}
}
int main(){}
The above code gives error like so:
"ComeauTest.c", line 3: error:
qualified name is not allowed in
namespace member
declaration
void M::f(){}
And
G++ also gives error.
But
VS2010 compiles fine.
My questions are:
a) What is the expected behavior?
b) $7.3.1.2 does not seem to talk about this restriction. Which portion of the Standard guides the behavior of such code?
Which portion of the Standard guides the behavior of such code?
C++03 Section $8.3 says
A declarator-id shall not be qualified except for the definition of a member function (9.3) or static data member (9.4) out-side of its class, the definition or explicit instantiation of a function or variable member of a namespace out-side of its namespace, or the definition of a previously declared explicit specialization outside of its name-space, or the declaration of a friend function that is a member of another class or namespace (11.4).
So your code is ill-formed.
However in discussing issue 548 the CWG agreed that the prohibition of qualified declarators inside their namespace should be lifted1.
1 : Active Issue 482
7.3.1.2-2 talks specifically about this:
Members of a named namespace can also be defined outside that namespace by explicit qualification (3.4.3.2) of the name being defined, provided that the entity being defined was already declared in the namespace and the definition appears after the point of declaration in a namespace that encloses the declaration’s namespace.
M::f is considered a outside of namespace definition.