I am trying to befriend a class in order for it to be able to reach a private constructor of it.
In some_file.h
class B;
namespace some_name {
class A {
public:
A() {}
private:
A (int x) {}
friend class ::B;
};
}
In other_file.h
#include "some_file"
namespace {
class B {
protected:
A* get_a(int x) { return new A(x); }
};
}
When compiling this code, I get -
error: 'some_name::A::A(int)' is private.
I now, it is private, this is why I befriended B.
What am I doing wrong here?
Can't you befriend your constructor?
Is there a namespace issue?
Thanks
Doing this:
namespace {
class B {
protected:
A* get_a(int x) { return new A(x) };
}
}
You're not putting B in the root (global) namespace but in an anonymous one.
So B can't be reached by ::B.
If you want B to be in the root (global) namespace, just don't enclose it with namespace at all. This should do the trick.
You only forward declared and friended a class B in the global namespace. Not a class B in a namespace whatever. You need to fully qualify the name of B.
Edit: Thanks ereOn.
I made a slight mistake. It's true that the reason that you've got a problem is because you've mis-declared and mis-referred-to B, but my original statement wasn't quite true. You need to take B out of the anonymous namespace - it's pointless being in a header anyway.
The problem is that you refer to B as ::B instead of B. Meaning, you're telling the compiler that B is a global name, but in fact it isn't: it's inside an anonymous namespace. You don't have to remove the anonymous namespace, it's just that it may not do what you expect it to do. Because the anonymous namespace is in a header it means what's inside that namespace is linked statically to any implementation file that includes the header. That's not very useful, because you hide nothing. You might as well remove that anonymous namespace.
Can't you befriend your constructor?
You can as shown below
struct B{
B();
void f();
};
struct A{
friend B::B();
private:
A(){}
};
B::B(){A a;} // fine
void B::f(){A a;} // error
int main(){
}
Related
I was shocked today by the fact that this code snippet has an ambiguous name reference:
class A
{
private:
typedef int Type;
};
class B
{
public:
typedef int Type;
};
class D : A, B
{
Type value;//error: reference to 'Type' is ambiguous
};
Hmm! Imagine you are the author of class A and your class has already been used everywhere by different people and different projects. One day you decide to rewrite your A class. Doesn't this mean that you cannot use any new (even private) name in your new class without breaking others' code?
What is the convention here?
Yes you are right. Using the current format/logic/coding style might break others' code if you decide to make changes later.
Try to use PIMPL or fully qualify symbols.
You're example is a good one of why it's not a good idea to not use fully qualify symbols.
It's the same issue with code like:
using namespace std;
The line of code above could break a lot of stuff.
As #Jarod42 mentioned it needs to follow pimpl idiom (pointer to implementation) to separate interface and implementation:
A.h
#include <memory>
class A
{
public:
A();
~A();
private:
class AImpl;
std::unique_ptr<AImpl> pimpl;
};
A.cpp
#include "A.h"
class A::AImpl
{
private:
typedef int Type;
/* .. */
};
A::A(): pimpl(new AImpl)
{
}
A::~A() = default;
main.cpp
#include "A.h"
class B
{
public:
typedef int Type;
};
class D : A, B
{
Type value;
};
I see no issue here, it's not because it's private that the symbol does not exists. If you try to access a private member the compiler tells you that you can't access it, not that does not exists.
Also, I would say it's a bad practise to use multiple inheritence and not fully qualify symbols.
class D : A, B
{
B::Type value;
};
It is possible to write a using declaration to e.g. promote a private base class method to being public in a derived class, i.e. B::foo in the example. Is it possible to do something similar to make a method available without the need to write another method?
#include <iostream>
class A
{
int m_number{ 99 };
protected:
int foo() { return m_number; }
};
class B : public A
{
public:
using A::foo;
};
class C
{
B m_a;
public:
using foo() = m_a.foo(); // is something like this possible?
};
int main() {
B b;
std::cout<<b.foo();
C c;
std::cout<<c.foo();
}
It is not possible to do this. In order to understand why, you need to understand that the purpose of a using declaration is to affect the name lookup algorithm, by making a name appear somewhere else from where it was originally declared.
The declaration in B that reads:
using A::foo;
has the effect of causing the lookup for the name foo in the scope of B to find the member A::foo. It does not create a new foo method that takes B* as the this pointer and forwards the call to A::foo; it simply causes the function A::foo to appear elsewhere (that is, in B).
A using declaration cannot result in the generation of any new code, such as that which would be necessary to forward a call by invoking a method on a member. If you want a new method, you'll just have to write it yourself.
The following code does not compile in gcc:
namespace One{
class A{
};
};
namespace Two{
class A{
public:
void what(){
cout << "Two::A says what!" << endl;
}
};
class B : public One::A{
public:
B(){
A xx;
xx.what();
}
};
};
And it gives:
gccbug.cpp: In constructor ‘Two::B::B()’:
gccbug.cpp:23: error: ‘class One::A’ has no member named ‘what’
Now, I was told that this is correct behavior (due to injected base name of One::A making A refer to One::A). However, this code compiles in C# (well, after changing a few things), so this seems to be c++ specific.
What I'm wondering is.. why? Is there a specific purpose for injecting the base name "One::A" as "A"?
The only reason I can think of is that in C++ you are likely to refer to the base class name in the initializer list of the constructor, like this:
namespace Two {
/*...*/
class B : public One::A {
public:
B():A()
{
/*...*/
}
};
}
Of course the purpose then is different from the one in your example, because you actually declare a local variable inside the constructor, whereas in my example, the A() refers to the object of type A that is implicit in the definition of class B due to inheritance.
However, the situation of my example is more likely to occur, so I guess they thought let's not require the namespace to be made explicit in this case. As a consequence, any reference to A without a namespace is interpreted as referring to the base class, rather than any other class named A, even if it is in the same namespace as the declaration of B.
Is there a specific purpose for injecting the base name "One::A" as "A"?
Yes. It is so that you could write this:
namespace N
{
class A
{
A *a;
};
}
In the absence of injected-name, you've to write N::A *a which is not nice.
Note that it is because of injected-name, the following lines are allowed:
A::A *a1; //ok
A::A::A *a2; //ok
A::A::A::A *a3; //ok
A::A::A::A::A *a4; //ok
//and so on
Online demo
By qualifying A with One:: you added the A from namespace one in scope, so the compiler will look there for it's name resolution.
Please educate me. Why does this compile:
struct compiles
{
struct A;
struct B
{
B(const A &a) : member(a.member) { }
int member;
};
struct A
{
A(const B &b) : member(b.member) { }
int member;
};
};
while this does not:
namespace doesnt
{
struct A;
struct B
{
B(const A &a) : member(a.member) { }
int member;
};
struct A
{
A(const B &b) : member(b.member) { }
int member;
};
}
(in MSVC 9.0)
The body of a class/struct/union definition is processed all at once, allowing references to members of classes defined later. A namespace is processed from top to bottom, and a forward declaration of struct A does not allow you to use its members without a definition. Try moving the definition of B's constructor out-of-class so you can put it after the definition of A.
In C++, class scope is special. Any declaration that extends to or past then end of the class definition is automatically extended to the regions defined by its member definitions (3.3.6 [basic.scope.class]).
This means that in the first case both the first declaration of struct A and the full definition of struct A are visible in the body of B and its constructor.
This doesn't apply to namespace scope so in the second case a.member in the constructor of B is an error because a definition of struct A isn't yet visible.
[Also tested with g++ 4.2] The first one compiles because the compiler fully picks up all the types defined within the struct before actually compiling the nested structures (think about public inline methods using private attributes that appear later within the class). Within the namespace the compiler simply works top-to-bottom and doesn't have special rules.
both will compile if you move the implementations of the constructors to the .cpp file where they should be anyway.
the following code will help me illustate my question to you directly:
#include<iostream>
class foo {
public:
class bar {
public:
bar(int a) : m_a(a) {}
void say() { std::cout << m_a << std::endl;}
private:
int m_a;
};
};
int main()
{
foo::bar b(3);
b.say();
}
as you see, to declare a object of class bar, we use the quite namespace like syntax "foo::bar", although actually bar is just an embebed class type in class foo. my question
is does the scope of a class itself is a namespace in c++?
No, a class is not a namespace. A class does form a declarative region, though.
You use the same syntax (the :: operator) to refer to names declared at class scope as you do to refer to names declared at namespace scope.
The class is not a namespace, it is a scope. You already used this term yourself. Namespace is a scope. Class is a scope as well. The :: operator is a scope resolution operator. Scope, not namespace, is the fundamental term that can act as a "common denominator" in this case. Scope is the reason why you can use the :: operator with both classes and namespaces on the left-hand side.
Another interesting distinction between classes and namespaces is that a namespace can be declared over multiple files and in multiple parts, but a class cannot. For example, you could do:
File a.hpp:
namespace Foo {
int memberA;
}
File b.hpp:
namespace Foo {
int memberB;
}
...
namespace Foo {
int memberC;
}
The scope of a class is not the same as the scope of a namespace. Classes can be templated, which affects the definitions in its scope. Classes can also have definitions that are only able to be used within that scope (private and protected).
The class name itself is not a namespace, although the scoping operator treats it as such, or almost as such anyways.