2014-12-26 7 views
1

Код, показанный ниже, не компилируется, когда функция друга mag() определена внутри класса, но работает, если определено вне класса (прокомментировано). Я думаю, что разница вызвана конструктором копирования, используемым для изменения типа аргумента от А до Б. Может ли кто-нибудь объяснить, почему я должен определять внешнюю функцию друга?функции функции друга и копирования

Кроме того, если класс 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); 

Если вы звоните mag с B объекта, Argument Dependant Lookup будет выглядеть в сферу B «s и найти определение.

Теперь, если 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

Если вы переместите определение 'mag' внутри' A' (и переадресовываете 'B' над' struct A'), то [код также компилируется.] (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<int> или B<float>

поэтому вы должны объявить магнум в качестве шаблона, а так:

// 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

, если вы хотите определить собственную функцию int mag, просто отредактируйте mag, чтобы это было так: void mag (const B & p) { std :: cout << "Mag Outside. \ n"; }Если вы хотите объявить mag для всех типов int, float, .. просто определите его внутри тела класса, как нормальную функцию, потому что не нужно, чтобы ключевое слово friend – 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 класс шаблон, вы должны добавить шаблон < ...> о как в объявлении функции и определения.

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

Разделы ++ Проект стандарта N3337 C, которые имеют отношение к этому вопросу:

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

В вашем примере, mag определяется в лексической области видимости класса B, т.е. имя mag не видно вне класса, даже если он имеет внешнюю связь. Чтобы сделать функцию видимой вне класса B, она должна быть объявлена ​​за пределами B.

 Смежные вопросы

  • Нет связанных вопросов^_^