Reputation: 1309
The code shown below doesn't compile when the friend function mag() is defined inside the class, but works if defined outside class (commented). I think the difference is caused by the copy constructor used to change the argument type from A to B. Can someone explain why I should define the friend function outside?
Moreover, if class B is a template class (adding template <class T>
at the top), defining the friend function outside will also not work.
#include <iostream>
using namespace std;
class A {
};
class B {
public:
B(const A& p) {
std::cout << "Copy/Conversion constructor" << std::endl;
}
friend void mag(const B& p) {
std::cout << "Mag Inside`.\n";
}
};
//void mag(const B& p) {
// std::cout << "Mag Outside.\n";
//}
int main() {
A a;
mag(a);
return 0;
}
Upvotes: 1
Views: 2676
Reputation: 206667
Sections of the C++ Draft Standard N3337 that are relevant for this question:
11.3 Friends
4 A function first declared in a friend declaration has external linkage (3.5). Otherwise, the function retains its previous linkage (7.1.1).
6 A function can be defined in a friend declaration of a class if and only if the class is a non-local class (9.8), the function name is unqualified, and the function has namespace scope. [ Example:
class M { friend void f() { } // definition of global f, a friend of M, // not the definition of a member function };
— end example ]
7 Such a function is implicitly
inline
. A friend function defined in a class is in the (lexical) scope of the class in which it is defined. A friend function defined outside the class is not (3.4.1).
In your example, mag
is defined in the lexical scope of class B
, i.e. the name mag
is not visible outside the class even though it has external linkage. To make the function visible outside the class B
, it has to be declared outside B
.
Upvotes: 2
Reputation: 29
1. if class B is not a template class, you must define mag function outside class because it is a friend function which means that it is a non-class-member-function.
class B{
public:
B(const A& p){
cout<<"copy constructor"<<endl;
}
friend void mag(const B& p);
private:
int k;
};
void mag(const B& p){
cout<<"mag"<<endl;
}
2. if class B is a template class, you should add template<...> statement both in function declaration and definition.
template<class T>
class B{
public:
B(const T& p){
cout<<"copy constructor"<<endl;
}
template<class T2> //declare mag as a template function
friend void mag(const T2& p);
};
template<class T> //define mag function
void mag(const T& p){
cout<<"mag\n";
}
Upvotes: 0
Reputation: 294
You can't define friend function like that for a class if that class was a template, because :
suppose you have for example B<int>
and B<float>
it will be ambiguous for the compiler whether to treat B as B<int>
or B<float>
therefore you must declare mag as a template as well like that :
// Inside the class :
friend void mag(const B<T>& p);
// Outside the class :
template <typename T> void mag(const B<T>& p) {
std::cout << "Mag Outside.\n";
}
then it will work!
Upvotes: 0
Reputation: 23813
Because the function mag
is not declared in the global scope (you did defined and declared it when you made it a friend at the same time, but the declaration in it's own scope is still required).
You need to declare it :
class B {
public:
B(const A& p) {
std::cout << "Copy constructor" << std::endl;
}
friend void mag(const B& p) {
std::cout << "Mag Inside`.\n";
}
};
void mag(const B& p);
If you call mag
with a B
object, Argument Dependant Lookup will look into B
's scope and find the definition.
Now if B
is a template, you'll need to declare each version of mag
with the appropriate parameters (and if several exist, you'll need to help the compiler to resolve ambiguities during conversions) :
template<typename T>
class B {
public:
B(const A& p) {
std::cout << "Copy constructor" << std::endl;
}
friend void mag(const B<T>& p) {
std::cout << "Mag Inside`.\n";
}
};
void mag(const B<int>& p); // Version for B<int> declared.
Upvotes: 5
Reputation: 16757
mag
is not declared in global scope. Also, ADL kicks in during the function overload resolution case, but it is strictly based on the types of the argument - not on implicity-convertible types. If you want to use ADL, call mag
with a B
.
int main() {
A a;
mag(B(a));
return 0;
}
Upvotes: 0