gpt4 book ai didi

c++ - 关于 C++ 名称查找的困惑

转载 作者:行者123 更新时间:2023-12-04 16:59:44 25 4
gpt4 key购买 nike

据我所知,嵌套作用域中的名称会在封闭作用域中隐藏相同的名称,如下所示:

namespace ttt {
class A {};

void test(const A&, int)
{
cout << "ttt::test()" << endl;
}
}

void test(const ttt::A&, int)
{
cout << "global::test()" << endl;
}

int main()
{
void test(const ttt::A&, int);
ttt::A a;
test(a, 1);
}
void test(const ttt::A&, int);的声明在主函数中隐藏了 namespace ttt 中的相同名称,所以控制台打印 global::test() (在 Visual Studio 2019 中测试)
但是,当我尝试下面的代码时:
std::ostream& operator<< (std::ostream& os, const string& str)
{
os << "global::operator" << endl;
return os;
}

int main()
{
std::ostream& operator<< (std::ostream & os, const string & str);
string a = "STD's operator";

cout << a << "STD's operator" << endl;
}
我试图重载 <<操作符是在 STL 中定义的模板,我自己的版本是 << .根据第一个例子,声明 operator<<在 main 中应该隐藏 << 的 STL 定义版本,那么所需的输出应该是
global::operator
global::operator
global::operator
或者编译错误,因为我不知道 endl可以转换为 string .
然而,该计划的结果是:
global::operator
STD's operator

所以第二个也是最后一个 <<在声明 cout << a << "STD's operator" << endl;调用 STL 的 << ,不是我定义的重载。不应该是 <<已被声明隐藏 std::ostream& operator<< (std::ostream & os, const string & str);在主要?
有人可能会说 "STD's operator"const char* ,以便 Argument Dependent Lookup(ADL) 添加一个更好的候选者,即 std::ostream& operator<< (std::ostream&, const char*)来自 std命名空间。如果这是真的,那么如何解释第一个例子。第一个示例中的 ADL 过程可能会添加 ttt::test(const A&, int)进入重载候选者,这会导致第一个例子中的歧义,但这并没有发生, ttt::test(const A&, int)只是被隐藏了。
“C++ 入门 5”的第 798 页说“当我们将一个类类型的对象传递给一个函数时,除了正常的范围查找之外,编译器还会搜索定义参数类的命名空间”。我想我的困惑是关于除了的准确含义。
如果这意味着类的命名空间与调用函数的作用域具有相同的优先级,那么第一个示例应该会引起歧义。
如果这意味着该类的命名空间具有较低的优先级,则所有 <<在第二个例子中的 main 函数中应该被我定义的版本隐藏。
如果这意味着类的命名空间具有更高的优先级,那么第一个示例应该打印 "ttt::test()" .
所以发生了什么事?

最佳答案

如果候选函数之一是在块范围内声明的,例如您的块范围函数声明,则不会执行 ADL。 (进行ADL时,在选择最可行的候选者时确实没有特殊的优惠待遇)
所以你的第一个例子,void test(const ttt::A&, int); (这是 ::test 的块作用域声明),意味着 ttt::test不再是候选人,全局::test被调用(删除块范围声明使其不明确)

我相信你在这里是正确的,第二个例子应该在构造一个 std::string 后调用你的全局运算符。 .
编译器似乎同意 std::cout << a << "STD's operator" << std::endl编译为等效于 std::operator<<(::operator<<(std::cout, a), "STD's operator").operator<<(std::endl) . << std::endl很好,因为它是通过成员查找而不是 ADL 找到的。问题是仍然使用ADL来查找std::operator<<(std::ostream&, const char*)直接从 C++11 标准中读取应该发生的情况:
“表达式中的运算符”[over.match.oper]p2:

If either operand has a type that is a class or an enumeration, a user-defined operator function might be declared that implements this operator [...]. In this case, overload resolution is used to determine which operator function or built-in operator is to be invoked to implement the operator. Therefore, the operator notation is first transformed to the equivalent function-call notation as summarized in Table 11 (where @ denotes one of the operators covered in the specified subclasses)


两个操作数都是类类型,因此按预期考虑用户定义的运算符函数。表 11 的相关子条款是 13.5.2,表达式 a@b , 即 (a).operator@(b)作为成员函数和 operator@(a, b)作为非成员函数。
[over.match.oper]p3

[...] for a binary operator @ with a left operand of type cv1 T1 and a right operand of type cv2 T2, three sets of candidate functions, designated member candidates, non-member candidates and built-in candidates, are constructed as follows:
[...]
The set of non-member candidates is the result of the unqualified lookup of operator@ in the context of the expression according to the usual lookup in unqualified function calls (3.4.2) except that all member functions are ignored.


其中 §3.4.2 是 [basic.lookup.arg] “参数相关名称查找”。
[basic.lookup.arg]p3 说:

Let X be the lookup set produced by unqualified lookup (3.4.1) and let Y be the lookup set produced by argument dependent lookup (defined as follows). If X contains

  • a declaration of a class member, or
  • a block-scope declaration that is not a using-declaration, or
  • a declaration that is neither a function or a function template

then Y is empty. [...] The set of declarations found by the lookup of the name is the union of X and Y.


只看 std::cout << "STD's operator"为简单起见,查找的名称是 operator<< .非限定查找找到 std::ostream& operator<< (std::ostream & os, const string & str); 的块作用域声明only(并且所有其他声明都是隐藏的)。但是由于找到了块作用域中的函数声明,ADL 不应该发生,并且没有其他非成员候选者。
因此候选函数集只是全局 ::operator<<(std::ostream & os, const string &)和成员(member) operator<<std::ostream及其基类,其中全局函数是最可行的。
编译器在查找操作符时似乎忽略了这个规则,即使有块作用域声明也总是执行 ADL。写作 operator<<(std::cout, "STD's operator")正确执行并输出 global::operator .

关于c++ - 关于 C++ 名称查找的困惑,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66047013/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com