2014-12-26 47 views
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; 
} 
+0

'B(const A&)'是一个转换构造函数,不是复制构造函数。您也没有指定编译时得到的错误。 –

+0

错误是'错误:使用未声明的标识符'mag''。 – danny

回答

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

如果调用magB对象,Argument Dependant Lookup将考虑B的范围,并找到定义。

现在,如果B是一个模板,你需要申报的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. 
+0

好的,但它是不够的,只是定义一个成员函数作为一个'朋友'给它的全球范围?如果没有复制构造函数,f.i直接将B对象传递给mag,不需要在外部声明mag。 – danny

+0

不,这是不够的。第二种情况只是因为[依赖于参数的查找](http://en.cppreference.com/w/cpp/language/adl),因此对于其他任何情况,全局范围声明仍然缺失。 – quantdev

+0

好的谢谢。如果B类是一个模板类,定义外面的函数似乎不起作用。 – danny

0

mag未在全局范围内声明。此外,ADL在函数重载解析期间踢入,但它严格基于参数的类型 - 而不是implicity-convertible类型。如果您想使用ADL,请拨打mag并拨打B

int main() { 
    A a; 
    mag(B(a)); 
    return 0; 
} 
+1

如果你在'A'里面移动'mag'的定义(并且在'struct A'上面向前声明'B'),那么[代码也会被编译。](http://coliru.stacked-crooked.com/a/ 629890ef0c3645b3) – 0x499602D2

+0

@ 0x499602D2同意。尽管如果打算使用ADL,而不污染全局命名空间,“mag”属于“B”的定义。 – Pradhan

+0

我不确定你的意思是“不含隐式可转换类型”。 ADL没有踢过[在我的例子中](http://coliru.stacked-crooked.com/a/629890ef0c3645b3)并隐式地将'A'转换为'B const&'? – 0x499602D2

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

然后它会工作!

+1

它不工作,除非我做mag (a)。如果我仍然可以称它为mag(a) – danny

+0

如果你想定义ur自己的int函数,只需编辑mag就可以这样: void mag(const B &p){ std :: cout <<“Mag Outside。\ n”; } 如果你想声明mag为所有类型int,float,..只是在类体内定义它,就像一个正常的函数,因为不需要关键字朋友 – Maher

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类是一个模板类,你都应该在函数声明和定义添加模板< ...>语句。对于这个问题是相关的C++标准草案N3337的

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"; 
} 
+0

“必须定义函数mag在类之外”是不正确的。但如果将其更改为“必须在类之外声明函数mag”,那么它适用于此特定情况。也许增加一些资格来说明这一点。 –

+0

@ Cheersandhth.-alf -----请阅读我上面的摘录代码的评论。这就是我的意思是一个函数定义,即函数定义是一个函数声明完整的正文。 – ciremai

2

节:

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

在你的榜样,magB类的词汇范围,定义,即名称mag是不可见即使有外部联系,也不在课堂上。为使该功能在类B之外可见,必须在B之外声明。