std::ranges
的算法时,就不会选择std::
中的重载函数。上面的示例已关闭 ADL,因此调用直接进入 std::ranges::find。25.2.2
The entities defined in thestd::ranges
namespace in this Clause are not found by argument-dependent name lookup (basic.lookup.argdep). When found by unqualified (basic.lookup.unqual) name lookup for the postfix-expression in a function call, they inhibit argument-dependent name lookup.
void foo() { using namespace std::ranges; std::vector<int> vec{1,2,3}; find(begin(vec), end(vec), 2); // #1 }
The function call expression at
#1
invokesstd::ranges::find
, notstd::find
, despite that (a) the iterator type returned frombegin(vec)
andend(vec)
may be associated withnamespace std
and (b)std::find
is more specialized ([temp.func.order]) thanstd::ranges::find
since the former requires its first two parameters to have the same type.
namespace mystd
{
class B{};
class A{};
template<typename T>
void swap(T &a, T &b)
{
std::cout << "mystd::swap\n";
}
}
namespace sx
{
namespace impl {
//our functor, the niebloid
struct __swap {
template<typename R, typename = std::enable_if_t< std::is_same<R, mystd::A>::value > >
void operator()(R &a, R &b) const
{
std::cout << "in sx::swap()\n";
// swap(a, b);
}
};
}
inline constexpr impl::__swap swap{};
}
int main()
{
mystd::B a, b;
swap(a, b); // calls mystd::swap()
using namespace sx;
mystd::A c, d;
swap(c, d); //No ADL!, calls sx::swap!
return 0;
}
cppreference的描述:
本页面描述的函数式实体是Niebloids,即:
- 在调用它们时不能指定显式模板参数列表。
- 它们中没有一个对参数相关查找可见。
- 当通过函数调用运算符左侧的名称进行正常未限定查找找到其中一个时,它会抑制参数相关查找。
Niebloids不可见于参数相关查找(ADL),因为它们是函数对象,而ADL仅适用于自由函数而不是函数对象。第三点是标准中示例中发生的情况。
find(begin(vec), end(vec), 2); //unqualified call to find
find()
调用未限定,因此在查找开始时,它会找到std::ranges::find
函数对象,这反过来阻止了ADL的发生。
继续搜索,我发现这个,在我看来是关于niebloids和CPOs(自定义点对象)最易理解的解释:
……一个CPO是一个对象(不是函数);它可被调用;它是constexpr-constructible的,[...]它是可定制的(这就是“与程序定义类型交互”的含义);并且它是概念约束的。
[...]
如果从上面删除“可定制,概念约束”这些形容词,那么你就得到了一个关闭ADL的函数对象——但不一定是一个自定义点。C++2a范围算法(例如std::ranges::find
)就是这样。 任何可调用的、constexpr-constructible对象都通俗地称为“niebloid”,以纪念Eric Niebler。
从 cppreference 网站上了解到:
本页面描述的函数实体是niebloids,也就是:
调用它们时不能指定显式模板参数列表。
它们都不可见于参数相关的查找。
当通过函数调用运算符左侧的名称进行正常非限定查找时,其中之一会抑制参数相关的查找。
在实践中,它们可以作为函数对象或使用特殊的编译器扩展来实现。