Why does a C++ friend class need a forward declaration only in other namespaces?
C++
Standard ISO/IEC 14882:2003(E)
7.3.1.2 Namespace member definitions
Paragraph 3
Every name first declared in a
namespace is a member of that
namespace. If a friend declaration in
a non-local class first declares a
class or function
(this implies that the name of the class or function is unqualified) the friend class
or function is a member of the
innermost enclosing namespace.// Assume f and g have not yet been defined.
void h(int);
template <class T> void f2(T);
namespace A {
class X {
friend void f(X); // A::f(X) is a friend
class Y {
friend void g(); // A::g is a friend
friend void h(int); // A::h is a friend
// ::h not considered
friend void f2<>(int); // ::f2<>(int) is a friend
};
};
// A::f, A::g and A::h are not visible here
X x;
void g() { f(x); } // definition of A::g
void f(X) { /* ... */} // definition of A::f
void h(int) { /* ... */ } // definition of A::h
// A::f, A::g and A::h are visible here and known to be friends
}
Your friend class BF;
is a declaration of A::BF
in namespace A rather than global namespace. You need the global prior declaration to avoid this new declaration.
How come forward declaration is not needed for friend class concept?
You're right, the friend declaration is kind of like a forward declaration.
The following compiles:
class A;
class B
{
friend A;
};
or
class B
{
friend class A;
};
this does not:
class B
{
friend A;
};
It's not actually the friend
declaration that forward-declares class A
, but the class
keyword. That's why the second example doesn't work, because it doesn't know what A
is. If you declare A
beforehand, like in the first snippet, it can resolve A
to a class declaration.
I stand corrected.
Declare a member-function of a forward-declared class as friend
As @Ben says, it's not possible, but you can give specific access just to that member function through a "passkey". It works a bit like the intermediate helper class, but is imho clearer:
// Storage.h
// forward declare the passkey
class StorageDataKey;
class Storage {
int data_;
public:
int data() { return data_; }
// only functions that can pass the key to this function have access
// and get the data as a reference
int& data(StorageDataKey const&){ return data_; }
};
// BigComplicatedClass.cpp
#include "BigComplicatedClass.h"
#include "Storage.h"
// define the passkey
class StorageDataKey{
StorageDataKey(){} // default ctor private
StorageDataKey(const StorageDataKey&){} // copy ctor private
// grant access to one method
friend void BigComplicatedClass::ModifyStorage();
};
void BigComplicatedClass::ModifyStorage(){
int& data = storage_.data(StorageDataKey());
// ...
}
a class-key must be declared when declaring a friend
I was surprised about this (and as a result deleted a previous incorrect answer). The C++03 standard says in 11.4:
An elaborated-type-specifier shall be used in a friend declaration for a class.
Then to make sure there's no misunderstanding, it footnotes that with:
The class-key of the elaborated-type-specifier is required.
GCC is the only compiler that I have that complains about the missing class-key, but it looks like other compilers are letting us get away with something non-standard...
Now as for the rationale - you'd have to ask someone who knows more about compilers (or standards) than I do.
Why doesn't the compiler like all the strings that are associated with Iter?
You need a forward declaration
class Iter;
outside the declaration ofclass ContainerPerson
. Otherwise the friend class isContainerPerson::Iter
which is unrelated to theIter
you declare later. See Why does a C++ friend class need a forward declaration only in other namespaces?Typo in the definition of
Iter* ContainerPerson::CreateIterator()
: should becreateIterator
, with lower casec
.Typo in
for(it->first; ...)
; should beit->first()
.Your iterations are mixed up. They should be
for(it->first(); !it->isDoneEnd(); it->next()) {
// ...
}
for(it->end(); !it->isDoneBegin(); it->prev()) {
// ...
}
With these fixed it compiles and runs cleanly: https://godbolt.org/z/3K7T7d14h
Bonus bug for you to fix later:
You never delete
the Iter
which is allocated in ContainerPerson::createIterator()
. That's a memory leak.
Forward declaration of nested types/classes in C++
You can't do it, it's a hole in the C++ language. You'll have to un-nest at least one of the nested classes.
Why do I need to forward declare class foo but not class bar, even though 'foo.h' and 'bar.h' are both included?
The problem is that you've a circular dependency with include files:
global.h includes level.h
level.h includes global.h
What happens is
- Compiler starts reading
level.h
- Reads
pragma once
that marks the file as already included - Finds inclusion of
global.h
and starts reading it (at that point) - Reads
pragma once
that marks the file as already included - Finds inclusion of
level.h
but ignores it (it was marked included in 2) - Keeps on reading the rest of
global.h
but the classLevel
is unknown
If the project is not trivial you should make a diagram of your classes and modules and decides what depends on (is built on) what. This diagram should be a DAG (without loops). More specifically it should be possible to subdivide the modules in "layers" in which no module from a lower layer depends on a module in an higher layer.
If you have a loop in your dependency diagram the project will be much harder to deal with (e.g. unit testing) because your modules are not really modules but the whole program becomes just a huge ball of code.
Injection of a friend declaration into a namespace, Eckel, Vol1, pg:440?
C++11 Standard 7.3.1.2 (3) says:
"If a friend declaration in a nonlocal class first declares a class or function [footnote: this implies that the name of the class or function is unqualified] the friend class or function is a member of the innermost enclosing namespace."
So any unqualified friend function declaration introduces a free function in the innermost enclosing namespace; (probably) the book referes to this as to "injection into namespace".
Note that it is even possible to define (implement) a function inside friend declaration:
namespace Z
{
class C
{
friend void f(void){/*do something*/}
};
}
This friend declaration not only "injects" but also implements free function f in the namespace Z (f is not a member of class C itself!), so no other declarations or definitions are needed for Z::f.
As to your example,
friend void Y::func(Z::X*, int);
is qualified (prefixed with namespace/class name), so it doesn't declare a function, it only refers to a member function Y::func previously declared in class Y. Such friend declarations "injects" nothing.
Related Topics
How to Enable Visual Styles Without a Manifest
Initializing a C++ Std::Istringstream from an in Memory Buffer
How to Declare an Array of Objects Whose Class Has No Default Constructor
Get Function Names from Call Stack
Why Is "Operator Void" Not Invoked with Cast Syntax
Where in Qt Creator Do I Pass Arguments to a Compiler
Ternary Conditional and Assignment Operator Precedence
Waiting Thread Until a Condition Has Been Occurred
How to Get the Application Data Path in Windows Using C++
Constexpr Function Parameters as Template Arguments
Does Resizing a Vector Invalidate Iterators
How to Serialize Boost::Dynamic_Bitset
Spirit-Qi: How to Write a Nonterminal Parser
Is Msiopenproduct the Correct Way to Read Properties from an Installed Product
Difference Between Initialization of Static Variables in C and C++