danny
danny

Reputation: 1309

friend function and copy constructors

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

Answers (5)

R Sahu
R Sahu

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

ciremai
ciremai

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

Maher
Maher

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

quantdev
quantdev

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

Pradhan
Pradhan

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

Related Questions