Разница между правилами поиска для функции-друга, определенной внутри и вне класса - PullRequest
0 голосов
/ 29 августа 2018

следующий код:

struct X {
    X() {}
};

struct Y {
    Y() {}
    Y(X) {}
    Y(int) {}
    friend bool operator==(const Y&, const Y&) { return false; }
};

bool f()
{
    return 1 == X();
}

не компилируется со следующей ошибкой:

error: no match for 'operator==' (operand types are 'int' and 'X')
     return 1 == X();

Если я переместу определение operator== за пределы класса, оно будет работать нормально:

struct X {
    X() {}
};

struct Y {
    Y() {}
    Y(X) {}
    Y(int) {}
    friend bool operator==(const Y&, const Y&);
};

inline bool operator==(const Y&, const Y&) { return false; }

bool f()
{
    return 1 == X();
}

Может кто-нибудь объяснить, почему? (В идеале, с некоторой цитатой из стандартного и понятного человеку объяснения / мотивации.) В ответе здесь: https://stackoverflow.com/a/20114792/1350936 @rightfold упомянул, что

Функции, определенные вне класса, можно найти даже без ADL

Но я не совсем понимаю, что это значит.

1 Ответ

0 голосов
/ 29 августа 2018

Следует отметить, что правила поиска для функций-друзей внутри и вне класса различны, см. [namespace.memdef] (выделено мной)

Если объявление друга в нелокальном классе сначала объявляет класс, функция, шаблон класса или шаблон функции друг член внутреннего вложенного пространства имен. Декларация друга само по себе не делает имя видимым для неквалифицированного поиска или квалифицированный поиск . [ Примечание: Имя друга будет видно в его пространство имен, если соответствующая декларация предоставляется в области пространства имен (или до или после определения класса предоставления дружбы). - конец примечания ] Если вызывается функция или шаблон функции друга, ее имя может быть найдено по имени поиска, который рассматривает функции из пространства имен и классы, связанные с типами функции аргументы ( [basic.lookup.argdep] ). Если имя в декларации друга не является ни квалифицированным, ни шаблоном-идентификатором , и объявление является функция или подробный спецификатор типа , поиск для определения было ли предприятие ранее объявлено, не должно рассматриваться области видимости вне самого внутреннего вмещающего пространства имен. [ Примечание: Другой формы объявлений друзей не могут объявить нового члена самое внутреннее пространство имен и, следовательно, следует обычным правилам поиска. - конечная нота ]

Это означает, что в вашем первом примере компилятор видит сравнение с операндами int и X, но нет жизнеспособного преобразования из X в int (или из int в X, но X также не имеет оператора сравнения). Преобразование обоих операндов в Y не предпринимается, поскольку соответствующий оператор сравнения не отображается в соответствии с приведенным выше предложением.

В то же время вы можете видеть, как опасно иметь неявные конструкторы во втором примере, потому что оба операнда неявно преобразуются в возможно не связанный тип Y. Это может привести к очень неожиданному поведению, потому что код, который не должен быть скомпилирован из-за семантической некорректности, считается допустимым компилятором. См. Также Руководство по ядру C ++ C.46: по умолчанию объявляют конструкторы с одним аргументом явным .

...