2015-07-02 4 views
3

Рассмотрим:Разница между друзьями, определенными в классе и снаружи: ошибка или функция?

struct Y { 
    Y(float f) : f(f) {} 
    float f; 
}; 

struct X { 
    X(Y y) : i(y.f) {} 
    int i; 
    friend bool operator==(X x1, X x2) { 
    return x1.i == x2.i; 
    } 
}; 

int main() 
{ 
    return Y(1) == Y(2); // ERROR 
} 

Это вызывает следующую ошибку на MSVC и похожий один на Clang:

'==': candidate function(s) not accessible 
could be the friend function at '..\main.cpp(11)' : '==' [may be found via argument-dependent lookup] 

Если я двигаю определение функции друг из класса:

struct X { 
    X(Y y) : i(y.f) {} 
    int i; 
    friend bool operator==(X x1, X x2); 
}; 

inline bool operator==(X x1, X x2) 
{ 
    return x1.i == x2.i; 
} 

Выражение в main() выше компилируется отлично.

Является ли это обязательным стандартом или ошибкой? Если это предусмотрено: почему?

+1

Обнаружено обман слишком поздно –

ответ

5

Это странность C++.

[C++14: 11.3/7]:[..] Функция друг определен в классе находится в (лексической) области видимости класса, в котором она определена. Функция друга, определенная вне класса, не является (3.4.1).

Это означает, что вызов оператора будет работать, если вы сделали это изнутри функции-члена, but not otherwise. Хотя это не член.

Аргумент-зависимый поиск - это единственный способ его вызова, но это бесполезно для вас здесь, потому что оба аргумента: X s, а не Y s.

Ваше обходное решение - лучший способ сделать это и позволяет избежать этого беспорядка (как вы видели).

+0

Так что это стандарт. Но почему*? –

+0

@ MarcMutz-mmutz, потому что делает * смысл *. Вещи имеют объем, поэтому вы не можете внезапно нанести ущерб тем местам, которые вы не коснулись. Если вы хотите, чтобы что-то произошло только внутри области класса, вы бы сделали это внутри этого класса. Если вы хотите сделать что-то глобально, вы сделаете это снаружи. –

+0

@ MarcusMüllerꕺꕺ: Область обычно определяется декларацией, а не определением. Если вы определяете функцию в некоторой области пространства имен 'NS', а затем определяете ее в неназванном пространстве имен в' NS', вы создали два имени, а не одно. Если, otoh, вы объявляете функцию друга в классе 'NS :: C', а затем определяете ее в' NS', у вас есть только одно имя. Чтобы быть ясным: я могу понять, почему функция друга, объявленная или определенная в 'NS :: C', должна находиться в области' NS :: C', но я не вижу смысла в определении упомянутого друга, внезапно меняющегося объем. Вместо этого вы всегда можете дружить с NS :: func() '. –

0

Я уверен, что это утвержденный стандарт. Здесь, в университете, я посещаю всякий раз, когда мы использовали функции друзей, он всегда был вне класса. Я не уверен на 100%, почему я сам, но я знаю, что он работает, не вызывая проблем.