Here is the example :
struct A
{
A(const int a ):b(a)
{
}
int b;
};
struct B
{
B() : a(5)
{
}
static void A()
{
}
A a;
};
int main()
{
B::A();
}
And the compiler error is :
a9.cpp:19: error: ‘A’ does not name a type
a9.cpp: In constructor ‘B::B()’:
a9.cpp:24: error: class ‘B’ does not have any field named ‘a’
I am using gcc 4.3.0 on fedora 9.
Can someone explains why is the compiler complaining?
If possible, with references from the standard.
Thanks
This works:
struct B {
B() : a(5) { }
static void A() { }
::A a;
};
Since you've used A as a member name in B, that member's definition shadows the A type from the outer namespace. Using :: you can get to that namespace.
This behavior is specified in the (draft) standard as:
3.3.7 (1) "A name can be hidden by an explicit declaration of that same name in a nested declarative region" (the definition of struct B, which is nested in the namespace where struct A is also defined).
Carefully read the introduction to chapter 3, Basic concepts, for further clarification. Especially, this section specifies that
3 (7) Two names are the same if
they are identifiers composed of the same character sequence; or
they are the names of overloaded operator functions formed with the same operator; or
they are the names of user-defined conversion functions formed with the same type.
Note that this last definition does not distinguish between types and class members, so the name hiding (shadowing) rule 3.3.7 (1) applies.
Related
I have the following code:
namespace A {
struct Foo {
int a;
};
}
struct Foo {
int b;
};
struct Bar : public A::Foo {
Bar(Foo foo) {
c = foo.b;
}
int c;
};
C++ compilers complains at "c = foo.b" because A::Foo does not have a member named b.
If I change the type of Bar parameter with ::Foo it works.
My question is what is the rational behind this behaviour (I suppose it has to do with the fact that the inheritance makes Bar enter the A namespace but I cannot find any documentation to support this theory.
Every class has its name injected into it as a member. So you can name A::Foo::Foo. This is called the injected class name.
[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.
[basic.lookup]
3 The injected-class-name of a class is also considered to be a
member of that class for the purposes of name hiding and lookup.
Because unqualified name lookup of the argument type begins in the scope of the class Bar, it will continue into the scope of its base class to account for any member there. And it will find A::Foo::Foo as a type name.
If you want to use the global type name, simply qualify it by its surrounding (global) namespace.
Bar(::Foo foo) {
c = foo.b;
}
Which is doing fully qualified lookup in a scope where the injected class name doesn't appear.
For a followup "why" question see
Why is there an injected class name?
Not a complete answer, only code that shows (since it compiles) that Bar does not enter the namespace A. You can see that when inheriting from A::Foo1 there is no problem with ambiguity of Foo which would be different if this inheritance lets Bar enter A.
namespace A {
struct Foo {
int a;
};
struct Foo1 {
int a;
};
}
struct Foo {
int b;
};
struct Bar : public A::Foo1 {
Bar(Foo foo) {
c = foo.b;
}
int c;
};
Please consider the following code:
struct A
{
void f()
{
}
};
struct B1 : A
{
};
struct B2 : A
{
};
struct C : B1, B2
{
void f() // works
{
B1::f();
}
//using B1::f; // does not work
//using B1::A::f; // does not work as well
};
int main()
{
C c;
c.f();
return 0;
}
I kindly ask you not to copy paste a standard reply on how to solve the diamond problem ("use virtual inheritance"). What I am asking here is why doesn't a using-declaration work in this case. The exact compiler error is:
In function 'int main()':
prog.cpp:31:6: error: 'A' is an ambiguous base of 'C'
c.f();
I got the impression a using-declaration should work from this example:
struct A
{
void f()
{
}
};
struct B
{
void f()
{
}
};
struct C : A, B
{
using A::f;
};
int main()
{
C c;
c.f(); // will call A::f
return 0;
}
Someone else can find the standard quote but I'm going to explain conceptually.
It doesn't work because a using-declaration only affects name lookup.
Your using-declaration causes name lookup to succeed where it would otherwise fail, that is, it tells the compiler where to find the function f. But it does not tell it which A subobject f acts on, that is, which one will be passed as the implicit this parameter when f is called.
There is only a single function A::f even though there are two A subobjects of C, and it takes an implicit this argument of type A*. In order to call it on a C object, C* must be implicitly converted to A*. This is always ambiguous, and is not affected by any using-declarations.
(This makes more sense if you put data members inside A. Then C would have two of each such data member. When f is called, if it accesses data members, does it access the ones in the A subobject inherited from B1, or the ones in the A subobject inherited from B2?)
There's a note in [namespace.udecl]/p17 that addresses this situation directly:
[ Note: Because a using-declaration designates a base class member
(and not a member subobject or a member function of a base class
subobject), a using-declaration cannot be used to resolve inherited
member ambiguities. For example,
struct A { int x(); };
struct B : A { };
struct C : A {
using A::x;
int x(int);
};
struct D : B, C {
using C::x;
int x(double);
};
int f(D* d) {
return d->x(); // ambiguous: B::x or C::x
}
—end note ]
In addition to T.C.'s answer, I'd like to add that the name lookup in derived class is explained in the standard pretty much in detail in section 10.2.
Here what is said about processing of using-declarations :
10.2/3: The lookup set (...) consists of two component sets: the declaration set, a set of members named f; and the subobject set, a set of subobjects where declarations of these members (possibly including
using-declarations) were found. In the declaration set, using-declarations are replaced by the members they designate, and type declarations (including
injected-class-names) are replaced by the types they designate.
So when you try to declare in struct C
using B1::f; // you hope to make clear that B1::f is to be used
according to the lookup rules, your compiler nevertheless finds the possible candidates: B1::f and B2::f so that it's still ambiguous.
In the grammar of C++ Specification, the members of a class is defined as this:
member-declaration:
decl-specifier-seq(optional) member-declarator-list(optional);
function-definition ;(optional)
::(optional) nested-name-specifier template(optional) unqualified-id ;//what is this?
using-declaration
template-declaration
...
I understand 4 of them. But the 3rd one defines a mandatory nested name specifier followed by an id. e.g
class {
X::Y::z;
}
I don't aware of any C++ syntax that matches this definition. Did I miss something?
The answer is to be found in the [class.access.dcl] section. To put shortly, such a declaration is called 'access declaration' and its purpose is to change access level for an inherited member.
For example:
class A
{
protected:
int a;
int a1;
};
class B
{
public:
int b;
int b1;
};
class C : public A, private B
{
public:
A::a;
B::b;
}
int f()
{
C c;
c.a1; // access error: A::a1 is protected
c.b1; // access error: B is private base of A
c.a; // accessible because A::a is 'published' by C
c.b; // accessible because B::b is 'published' by C
}
This sort of declaration was superseded by using, but kept for compatibility purposes.
gcc accepts the following code, while clang rejects it.
struct S
{
struct Type
{
};
operator Type()
{
return Type();
}
};
void f(S& s)
{
s.operator Type(); // error: unknown type name 'Type'
}
The standard says Type is "looked up in the class of the object expression" S. It seems gcc includes the members of S in the search, while clang considers only S and its base classes doesn't. Which is correct?
Relevant quote from C++ Working Draft N3337:
3.4.5 Class member access [basic.lookup.classref]/7
If the id-expression is a conversion-function-id, its conversion-type-id 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.
In this particular case gcc is right. The lookup rule dictates that lookup for Type should be performed first in the context of the type of the object then in the context of where the expression is used. The standard even provides an example, that although not exact is similar to yours:
struct A { };
namespace N {
struct A {
void g() { }
template <class T> operator T();
};
}
int main() {
N::A a;
a.operator A(); // calls N::A::operator N::A
}
Lookup starts inside ::N::A where it finds the injected name A and resolves it to be ::N::A.
In C++0x, you can use the using keyword to inherit constructors, like so:
class B { B(int) {} };
class A : public B { using B::B; };
Which will implicitly declare an A(int) constructor. Does this work with templates?
class B { B(int) {} };
template<class T> class A : public T { using T::T; };
Within T::T, I expect the compiler to figure out the left hand T since using the scope operator on template arguments is normal, but figuring out that the right hand T is the constructor is a special case. In fact it appears there's an ambiguity: what if I have a method called T in B that I'm trying to add overloads to in A (that's how a compiler would interpret such a using declaration pre-C++0x)?
Yes it works, and the reason is the name lookup mechanism. The mechanism a inheriting-constructors declaration works is simple: If the using declaration's name refers to base class constructors, that's an inheriting constructors declaration. At 3.4.3.1[class.qual]p2 we find:
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
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-specifier
the name is instead considered to name the constructor of class C.
This is the paragraph that makes out of class constructor definitions work, and this is also the paragraph that makes inheriting constructor declarations work. The second bullet applies in this case:
struct B {
B(int) { }
};
typedef B mytype;
struct A : B {
// "name ... is the same as the identifier ... in the last component ..."
using mytype::mytype;
};
template<typename T> using same = T;
struct C : B {
// "name ... is the same as the template-name ... in the last component ..."
same<B>::same;
};
The latter example proves also useful in cases such as the following
template<template<typename> class Base>
struct X : Base<int> {
using Base<int>::Base;
};
In summary:
The first bullet above is a semantic rule - if the name after the nested name specifier refers to the injected class name (B::B or mytype::B), then it will be translated to refer to the constructor(s).
The second bullet is a syntactic rule - the names just must match - their meaning is irrelevant otherwise - there could have been a member called Base in the template argument provided to X, such as in the following, but the using declaration would still import the constructors and do not name the member Base:
template<typename T> struct D { private: T Base; };
X<D> x; // valid, the private member is *not* touched!
Yes, it appears it does, from the standard (Feb 2011 Draft), section 12.9:
template< class T >
struct D : T {
using T::T; // declares all constructors from class T
~D() { std::clog << "Destroying wrapper" << std::endl; }
};
Class template D wraps any class and forwards all of its constructors,
while writing a message to the standard log whenever an object of
class D is destroyed. —end example
Another thing to note, while the standard allows it, according to this list, only 1 compiler, IBM XLC++, supports this feature in a release version. GCC only currently supports it with a patch.
Edit: AJG85 pointed out that the T in the template always refers to the placeholder, so the 'using T::T' always refers to the template argument.