2014-12-26 46 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之外聲明。