1

下面显示的代码在类内定义友元函数 mag() 时无法编译,但如果在类外定义(已注释)则有效。我认为差异是由用于将参数类型从 A 更改为 B 的复制构造函数引起的。有人可以解释为什么我应该在外面定义朋友函数吗?

而且,如果B类是一个模板类(template <class T>在顶部添加),在外面定义友元函数也是行不通的。

#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;
}
4

5 回答 5

4

因为该函数mag未在全局范围内声明(您确实在将其设为友元时定义并声明了它,但仍需要在它自己的范围内声明)。

你需要声明它:

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);

如果您mag使用B对象调用,Argument Dependent Lookup将查看B's 范围并找到定义。

现在 ifB是一个模板,您需要mag使用适当的参数声明每个版本(如果存在多个,您需要帮助编译器在转换过程中解决歧义):

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.
于 2014-12-26T23:45:10.290 回答
2

与此问题相关的 C++ 标准草案 N3337 的部分:

11.3 朋友

4 在友元声明中首先声明的函数具有外部链接 (3.5)。否则,该函数将保留其先前的链接(7.1.1)。

6 当且仅当该类是非本地类(9.8),函数名称是非限定的,并且该函数具有命名空间范围时,才能在类的友元声明中定义函数。[示例:

class M {
    friend void f() { } // definition of global f, a friend of M,
                        // not the definition of a member function
 };

—结束示例]

7 这样的功能是隐含的inline。在类中定义的友元函数在定义它的类的(词法)范围内。在类外定义的友元函数不是(3.4.1)。

在您的示例中,mag在 class 的词法范围内定义B,即名称mag在类外部不可见,即使它具有外部链接。要使函数在类外部可见B,必须在外部声明B

于 2014-12-27T04:57:31.140 回答
0

mag未在全局范围内声明。此外,ADL会在函数重载解析情况下发挥作用,但它严格基于参数的类型,而不是基于隐式可转换类型。如果您想使用 ADL,请mag使用B.

int main() {
    A a;
    mag(B(a));
    return 0;
}
于 2014-12-26T23:48:58.970 回答
0

如果该类是模板,则不能为该类定义这样的友元函数,因为:

假设您有例如B<int>B<float>编译器是否将 B 视为B<int>B<float>

因此,您必须像这样将 mag 声明为模板:

// 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";
}

那么它将起作用!

于 2014-12-27T00:00:08.670 回答
0

1.如果B类不是模板类,则必须在类外定义mag函数,因为它是友元函数,也就是非类成员函数。

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. 如果B类是一个模板类,你应该在函数声明和定义中添加template<...>语句。

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";
}
于 2014-12-27T02:45:00.183 回答