Moh*_*han 5 c++ boost argument-dependent-lookup
我正在尝试为另一个命名空间中定义的类型T定义一个等于运算符,然后使用相等运算符optional<T>
.在clang(Apple LLVM 9.1.0)上,此代码:
namespace nsp {
struct Foo {
};
}
bool operator==(const nsp::Foo& a, const nsp::Foo& b);
void foo() {
optional<nsp::Foo> a = none;
optional<nsp::Foo> b = none;
if (a == b)
;
}
Run Code Online (Sandbox Code Playgroud)
导致错误:
/usr/local/include/boost/optional/detail/optional_relops.hpp:29:34: error: invalid operands to binary expression ('const nsp::Foo' and 'const nsp::Foo')
{ return bool(x) && bool(y) ? *x == *y : bool(x) == bool(y); }
~~ ^ ~~
MWE.cpp:40:19: note: in instantiation of function template specialization 'boost::operator==<what3words::engine::nsp::Foo>' requested here
if (a == b)
^
/usr/local/include/boost/optional/detail/optional_relops.hpp:28:6: note: candidate template ignored: could not match 'optional<type-parameter-0-0>' against 'const nsp::Foo'
bool operator == ( optional<T> const& x, optional<T> const& y )
Run Code Online (Sandbox Code Playgroud)
发生了什么?我的猜测是它与Koenig查找规则有关...
做这个:
namespace nsp {
bool operator==(const Foo& a, const Foo& b);
}
Run Code Online (Sandbox Code Playgroud)
解决你的问题.
如果你有控制权Foo
,你可以改为:
namespace nsp {
struct Foo {
friend bool operator==(const Foo& a, const Foo& b) {
return true;
}
};
}
Run Code Online (Sandbox Code Playgroud)
如果Foo
是模板类,这是最佳的.
这里发生的optional
是在std
(或者boost
其他)中,并且在该命名空间中它尝试进行nsp::Foo == nsp::Foo
调用.
有一个==
不适用于::std
命名空间,所以它不会查找::
; 一旦它发现任何 ==
它停止寻找,即使参数完全不相关.它还==
在与参数关联的名称空间中查找- 在本例中::nsp
.但它也从未在::
这里看过.
将运算符添加到类型时,始终在类型的名称空间中定义运算符.
可以重新打开命名空间.因此,如果您无法控制头文件,则可以使用其中创建新的头文件==
.这==
必须在optional<nsp::Foo>::operator==
调用的每个点都可见,或者由于ODR违规而导致程序生成错误(在这种情况下,还会生成编译器错误,这对于避免heizenbugs很有用).
当您调用操作符(或函数)时,查找遵循几个简单的步骤.
首先,它在本地(在本地命名空间中)环顾四周.如果它在那里找到任何东西,则此搜索停止.(这包括using ns::identifier;
注入命名空间的名称,但通常不包括using namespace foo;
).即使函数或运算符不适用于所讨论的类型,也会发生"停止"; 任何==
类型的名称空间中的任何类型都会停止此搜索.
如果找不到匹配项,它将开始查找封闭的命名空间,直到找到函数/运算符或到达根命名空间.如果存在using namespace foo;
声明,则这些命名空间中的函数/运算符将被视为位于using namespace
要导入的位置和命名空间的"公共父"命名空间中.(因此using namespace std;
,namespace foo
它似乎std
是在::
,而不是foo
).
结果生成一组候选重载决策.
接下来,完成ADL(参数依赖查找).检查所有函数/运算符参数的关联名称空间.此外,还会检查(递归)模板的所有类型参数的关联名称空间.
收集与名称匹配的运算符/函数.对于ADL,不检查父命名空间.
这两个运算符/函数集合是您重载决策的候选者.
在您的情况下,==
调用的名称空间是boost
. boost
有大量的==
运营商(即使他们不适用),因此所有的==
中boost
考生.
接下来,我们检查参数的命名空间 - nsp::Foo
在本例中.我们看着nsp
,看不到==
.
然后我们对这些进行重载解析.没有候选人工作,并且您得到编译器错误.
现在,当您将用户定义的内容移动==
到时namespace nsp
,它会被添加到==
ADL步骤中找到的集合中.当它匹配时,它被称为.
重载决议(它与候选人的关系)是它自己的复杂主题.简短形式是它试图找到涉及最少量转换的重载; 如果两个案例完全匹配,则它更喜欢模板上的非模板和非变量的非变量.
"最少量的转换"和"确切"的内容有很多可以误导程序员的细节.最常见的是将Foo
左值转换为Foo const&
少量转换,将其转换为template<class T> T&&
或T&
不转换.
归档时间: |
|
查看次数: |
486 次 |
最近记录: |