We found something similar to the following (don't ask ...):
namespace N {
struct A { struct B; };
}
struct A { struct B; };
using namespace N;
struct ::A::B {}; // <- point of interest
Interestingly, this compiles fine with VS2005, icc 11.1 and Comeau (online), but fails with GCC:
global qualification of class name is invalid before '{' token
From C++03, Annex A, it seems to me like GCC is right:
the class-head can consist of nested-name-specifier and identifier
nested-name-specifier can't begin with a global qualification (::)
obviously, neither can identifier
... or am i overlooking something?
I think you are getting it right: GCC implements the standard to the letter in this case, while the others implement it less strict (have a look at issue #355).
You could do the following to work-around the limitation of the syntax
struct identity< ::A >::type::B {};
Or you use an explicit named typedef
typedef ::A AHidden;
struct AHidden::B { };
Or, of course, you exchange the order of using namespace and the nested class definition. Notice that Annex A is informative only. The normative text is at clauses 5.1/7 and 9.
Related
When I compile the following snippet with g++
template<class T>
class A
{};
template<class T>
class B
{
public:
typedef A<T> A;
};
the compiler tells me
error: declaration of ‘typedef class A<T> B<T>::A’
error: changes meaning of ‘A’ from ‘class A<T>’
On the other hand, if I change the typedef to
typedef ::A<T> A;
everything compiles fine with g++. Clang++ 3.1 doesn't care either way.
Why is this happening? And is the second behavior standard?
g++ is correct and conforming to the standard. From [3.3.7/1]:
A name N used in a class S shall refer to the same declaration in its
context and when re-evaluated in the completed scope of S. No
diagnostic is required for a violation of this rule.
Before the typedef, A referred to the ::A, however by using the typedef, you now make A refer to the typedef which is prohibited. However, since no diagnostic is required, clang is also standard conforming.
jogojapan's comment explains the reason for this rule.
Take the following change to your code:
template<class T>
class A
{};
template<class T>
class B
{
public:
A a; // <-- What "A" is this referring to?
typedef A<T> A;
};
Because of how class scope works, A a; becomes ambiguous.
I will add to Jesse's answer about the seemingly peculiar behavior of GCC in compiling:
typedef A<T> A;
vs
typedef ::A<T> A;
This also applies to using statements as well of the form:
using A = A<T>;
using A = ::A<T>;
What seems to be happening within GCC, is that during the compilation of the typedef/using statement declaring B::A, that the symbol B::A becomes a valid candidate within the using statement itself. I.e. when saying using A = A<T>; or typedef A<T> A; GCC considers both ::A and B::A valid candidates for A<T>.
This seems odd behavior because as your question implies, you don't expect the new alias A to become a valid candidate within the typedef itself, but as Jesse's answer also says, anything declared within a class becomes visible to everything else inside the class - and in this case apparently even the declaration itself. This type of behavior may be implemented this way to permit recursive type definitions.
The solution as you found is to specify for GCC precisely which A you're referring to within the typedef and then it no longer complains.
Consider
using foo = int;
struct A {
typedef A (foo)();
};
GCC and ICC accept the snippet, while Clang and MSVC reject it. Clang's error message is
<source>:4:15: error: function cannot return function type 'void ()'
typedef A (foo)();
^
<source>:4:13: error: typedef name must be an identifier
typedef A (foo)();
^
2 errors generated.
And MSVC says
<source>(4,15): error C2091: function returns function
typedef A (foo)();
^
(live demo)
Why do Clang and MSVC produce this error? Which compilers are correct?
(I'm specifically looking for quotation from the standard or any defect report.)
Both Clang and MSVC are ignoring the typedef specifier and reading the declaration as that of a constructor (that is, A is the constructor name) accepting parameter types (foo) (that is, (int)) and "returning" a function type signified by the trailing parentheses ().
Yes, constructors don't have return types; but if they did have return types they would have return type A, so the additional () at the end makes these compilers think that you now have a constructor with return type the function type A().
This is supported by noting that the following "similar" declarations have similar error messages:
A (foo)();
typedef ~A(foo)();
Also, by adding static we can get an illuminating error message from MSVC:
A static (int)();
error C2574: '(__cdecl *A::A(int))(void)': cannot be declared static
For workarounds: under Clang (but not MSVC) you can move the typedef specifier to the right, or use an elaborated type specifier:
A typedef (foo)();
typedef struct A (foo)();
Under all compilers you can remove or add parentheses:
typedef A foo();
typedef A ((foo))();
And you can always update to a type alias:
using foo = A();
Clang is wrong: foo in the typedef declaration in A does not refer to the namespace-scope typedef-name foo
W.r.t. the standard rules, the enclosing namespace/scope alias declaration
using foo = int;
is a red herring; within the declarative scope of class A it will be shadowed by names declared in A
#include <type_traits>
using foo = int;
struct A {
using foo = char;
foo x;
};
static_assert(std::is_same_v<foo, int>,"");
static_assert(std::is_same_v<A::foo, char>,"");
static_assert(std::is_same_v<decltype(A::x), char>,"");
The key here being that typedef A (foo)(); declares the name foo within the declarative region of A, as per [dcl.spec]/3 [emphasis mine]:
If a type-name is encountered while parsing a decl-specifier-seq, it is interpreted as part of the decl-specifier-seq if and only if there is no previous defining-type-specifier other than a cv-qualifier in the decl-specifier-seq.
Specifically, this means that in the typedef declaration
typedef A (foo)();
even if there is an existing typedef-name foo, that foo is not considered in the typedef declaration, namely it is not considered as a type-name part of the decl-specifier-seq of typedef A (foo)(), as A has already been encountered previous to it, and A is a valid defining-type-specifier. Thus, the original example:
using foo = int;
struct A {
typedef A (foo)();
};
can be reduced to:
// (i)
struct A {
typedef A (foo)(); // #1
};
which declares the typedef name foo in A (A::foo), where the paranthese around the name are redundant, and the typedef declaration at #1 can likewise be written as
// (ii)
struct A {
typedef A foo(); // #1
};
and can likewise be introduced using an alias declaration ([dcl.typedef]/2):
// (iii)
struct A {
using foo = A();
};
(i), (ii) and (iii) are accepted by both GCC and Clang.
Finally, we may note that Clang accepts the following program:
using foo = int;
struct A {
typedef A foo();
using bar = A();
};
static_assert(std::is_same_v<A::foo, A::bar>,"");
and that the root issue of the example of the OP is arguably a Clang bug, where Clang fails to adhere to [dcl.spec]/3 and interprets the outer-scope typedef-name foo as part of the decl-specifier-seq of the inner-scope typedef declaration, only for the case where the latter has wrapped the shadowed name foo in parantheses.
You are changing the meaning of foo from int to A() when you redeclare it inside A. This violates basic.scope.class#2:
A name N used in a class S shall refer to the same declaration in its context and when re-evaluated in the completed scope of S. No diagnostic is required for a violation of this rule.
Since this is IFNDR, all compilers are conforming.
Consider the following piece of code:
struct Foo {};
template<typename ForwardIterator>
struct Foo*
Bar(ForwardIterator first, ForwardIterator last)
{
(void)first;
(void)last;
Foo *foo(nullptr);
return foo;
}
The above piece of code compiles fine in Clangv3.5 and GCCv4.9.
However, it fails to compile in VC++2013.
Removing the struct identifier (see below) from the return type solves the problem:
struct Foo {};
template<typename ForwardIterator>
Foo*
Bar(ForwardIterator first, ForwardIterator last)
{
(void)first;
(void)last;
Foo *foo(nullptr);
return foo;
}
Q1:
Is this a visual studio bug?
Q2:
This issue came up, because in my code-base the Foo struct lies in a .h.c file (i.e., is a C struct) and in order to signify C/POD structs in my code, I use the struct identifier. Is this a bad idea (i.e., in C++ code I should avoid to use struct identifier in this fashion)?
The main point of an elaborated type specifiers is to allow you refer to a name that has been hidden, from the draft C++ standard section 3.4.4 Elaborated type specifiers:
An elaborated-type-specifier (7.1.6.3) may be used to refer to a
previously declared class-name or enum-name even though the name has
been hidden by a non-type declaration (3.3.10).
and so in the case where Foo is hidden, you would have to use an elaborated type specifiers:
struct Foo {};
void Foo()
{
}
I see nothing in 7.1.6.3 Elaborated type specifiers or 14 Templates that would prevent this use. In fact it looks like from the description of CS2989 Visual Studio is getting confused and thinks you are attempting to redefine a non-template class as a template class.
So this looks like a bug to me, so I would file a bug report.
Update
Filed a bug report.
When I compile the following snippet with g++
template<class T>
class A
{};
template<class T>
class B
{
public:
typedef A<T> A;
};
the compiler tells me
error: declaration of ‘typedef class A<T> B<T>::A’
error: changes meaning of ‘A’ from ‘class A<T>’
On the other hand, if I change the typedef to
typedef ::A<T> A;
everything compiles fine with g++. Clang++ 3.1 doesn't care either way.
Why is this happening? And is the second behavior standard?
g++ is correct and conforming to the standard. From [3.3.7/1]:
A name N used in a class S shall refer to the same declaration in its
context and when re-evaluated in the completed scope of S. No
diagnostic is required for a violation of this rule.
Before the typedef, A referred to the ::A, however by using the typedef, you now make A refer to the typedef which is prohibited. However, since no diagnostic is required, clang is also standard conforming.
jogojapan's comment explains the reason for this rule.
Take the following change to your code:
template<class T>
class A
{};
template<class T>
class B
{
public:
A a; // <-- What "A" is this referring to?
typedef A<T> A;
};
Because of how class scope works, A a; becomes ambiguous.
I will add to Jesse's answer about the seemingly peculiar behavior of GCC in compiling:
typedef A<T> A;
vs
typedef ::A<T> A;
This also applies to using statements as well of the form:
using A = A<T>;
using A = ::A<T>;
What seems to be happening within GCC, is that during the compilation of the typedef/using statement declaring B::A, that the symbol B::A becomes a valid candidate within the using statement itself. I.e. when saying using A = A<T>; or typedef A<T> A; GCC considers both ::A and B::A valid candidates for A<T>.
This seems odd behavior because as your question implies, you don't expect the new alias A to become a valid candidate within the typedef itself, but as Jesse's answer also says, anything declared within a class becomes visible to everything else inside the class - and in this case apparently even the declaration itself. This type of behavior may be implemented this way to permit recursive type definitions.
The solution as you found is to specify for GCC precisely which A you're referring to within the typedef and then it no longer complains.
The following snippet fails to compile with Visual Studio 2010, but GCC likes it:
namespace Test {
class Baz;
// Adding class Bar; here and removing the class below makes it work
// with VC++, but it should work like this, shouldn't it?
void Foo (Baz& b, class Bar& c);
}
namespace Test {
class Bar
{
// Making this method non-template works
template <typename T>
static void Lalala ()
{
}
};
}
int main ()
{
}
Am I doing something stupid here or is this a valid compiler bug? The error I get is:
error C2888: 'void Bar::Foo(void)' : symbol cannot be defined within namespace 'Test'
It compiles with GCC 4.5.1: http://ideone.com/7sImY
[Edit] Just to be clear, I want to know if this is valid C++ or not (and if so, why not) -- workarounds to get it compiled are nice but not part of this question.
Well, I tried it in codepad.org too and it compiles, but I'm not sure it should (not THAT proficient in C++ compiler functionality)!
Workaround: forward declare Bar as well or you have to define Bar before you make Foo. In other words, this compiles in MSVC:
namespace Test
{
class Baz;
class Bar;// also forward-declare Bar
void Foo (Baz& b, class Bar& c);
}
namespace Test
{
class Bar
{
template <typename T>
static void Foo ()
{
}
};
}
int main(void)
{
return 0;
}
Update:
I think that this might already be a bug reported to Microsoft... this looks pretty close: http://connect.microsoft.com/VisualStudio/feedback/details/99218/invalid-error-c2888-when-a-class-is-defined-after-it-is-declared
The workaround cited by Microsoft:
A stand-alone forward declaration consists of an elaborated type specifier followed by a semicolon.
insert the declaration
class C2888;
before the declaration of foo(C2888o, C2888).
I think the code is well-formed. But proving that for certain would require making sure there's nothing in the Standard that contradicts the usage.
Some relevant quotes from the C++11 Standard:
3.3.2 p6:
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 non-class, non-function-prototype scope that contains the declaration.
3.4.4 p2:
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-seq opt 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, or if the elaborated-type-specifier
appears in a declaration with the form:
class-key attribute-specifier-seq opt identifier ;
the elaborated-type-specifier is a declaration that introduces the class-name as described in 3.3.2.
7.1.6 has some syntax definitions establishing that an elaborated-type-specifier can syntactically be a type-specifier. 7.1 establishes that a type-specifier can syntactically be a decl-specifier, which is the syntactical unit used as the type in a function parameter-declaration (8.3.5).
Probably it is a compiler bug.
Changing the order of parameters will change the compilation result.
namespace Test {
void Foo (class Bar& b, class Baz& c) - will compile.
}
The class Bar construct is erroneous. Are you, by any chance, a C programmer who didn't use typedef struct { /* members */ } Foo?
Anywho, you need to define both Bar and Baz inside test:
namespace Test {
class Bar;
class Baz;
};
And remove class, struct, union, and enum keywords when declaring function parameters.
With that modification, it compiles cleanly in g++4.6.