我不明白为什么以下代码无法编译(例如在gcc 9.10或MS VS C ++ 2019中):
class X {
public:
friend bool operator==(int, X const &);
};
int main() {
2 == X(); // ok...
static_cast<bool (*)(int, X const &)>(&operator==); // Error: 'operator==' not defined
return 0;
}
但以下代码已编译,没有任何问题:
class X {
public:
};
bool operator==(int, X const &);
int main() {
2 == X(); // ok...
static_cast<bool (*)(int, X const &)>(&operator==); // OK!
return 0;
}
我期望X的朋友函数(operator ==)表现为独立函数(operator ==)。我缺少什么?谢谢
答案 0 :(得分:23)
我缺少什么?
内联好友声明不会使该函数可用于普通名称查找。
请密切注意该错误。它并不是说该函数的类型错误,它根本找不到名为operator==
的东西。这是设计使然。
仅argument dependent lookup找到内联朋友定义。普通查找(例如,命名函数以获取其地址)无法找到它。如果希望该功能可用于该目的,则必须提供一个命名空间范围的声明。
class X {
public:
friend bool operator==(int, X const &) { /* ... */ }
};
bool operator==(int, X const &);
答案 1 :(得分:5)
如果该函数附加到全局模块,则该函数暗含为内联([dcl.inline])函数。 在类中定义的朋友功能在其定义的类的(词汇)范围内。在类外定义的朋友函数不是([basic.lookup.unqual])。
根据标准namespace.memdef/3: (感谢@StoryTeller)
如果非本地类中的某个好友声明首先声明了一个类,函数,类模板或函数模板,则该好友是最内层封闭名称空间的成员。 朋友声明本身不会使名称显示给不合格的查找([basic.lookup.unqual])或合格的查找([basic.lookup.qual])。 [注意:如果在名称空间范围内提供了匹配的声明,则朋友的名称将在其名称空间中可见(在授予友谊的类定义之前或之后)。 —注释[end note]如果调用了朋友函数或函数模板,则可以通过名称查找来找到其名称,该名称查找将考虑来自与函数参数类型关联的名称空间和类中的函数([basic.lookup.argdep]) 。如果朋友声明中的名称既不是限定名称也不是模板ID,并且声明是函数或精化类型说明符,则用于确定该实体先前是否已声明的查找将不考虑最内部封闭的命名空间之外的任何范围。
以下内容不起作用,因为该功能在当前作用域中不可见。
static_cast<bool (*)(int, X const &)>(&operator==); // Error: 'operator==' not defined
答案 2 :(得分:3)
区别在于,在第一个代码段中,您仅在X
范围内声明了运算符,但在声明范围之外。 operator==(int,X const &)
中没有main
可访问。如果您修复了该问题并在外部也声明了它,则只会收到警告:
class X {
public:
friend bool operator==(int, X const &);
};
bool operator==(int,X const&); // <--
int main() {
2 == X(); // ok...
static_cast<bool (*)(int, X const &)>(&operator==); // Error: 'operator==' not defined
return 0;
}
但是请注意,对于这两种情况,您都需要定义才能实际调用运算符。
为说明起见,请考虑使用
struct foo {
friend void bar() {
std::cout << "this is a inline definition of friend funtion";
}
};
从bar
外部访问foo
的唯一方法是在foo
外部添加一个声明:
void bar();