Waq*_*med 58
术语niebloid来自Eric Niebler 的名字。简而言之,它们是禁止 ADL(参数相关查找)发生的函数对象,以便在调用std::
算法 from 时不会拾取in中的重载std::ranges
。
这是一条推文(来自 2018 年)和埃里克本人建议的名字的回答。Eric在 2014 年写了一篇文章解释了这个概念。
在标准文档本身中可以最好地看到它的实际效果:
25.2.2在本条款
的std?::?ranges
命名空间中定义的实体无法通过参数相关的名称查找 ( basic.lookup.argdep ) 找到。当在函数调用中通过非限定(basic.lookup.unqual)名称查找找到后缀表达式时,它们会禁止依赖于参数的名称查找。Run Code Online (Sandbox Code Playgroud)void foo() { using namespace std::ranges; std::vector<int> vec{1,2,3}; find(begin(vec), end(vec), 2); // #1 }
在函数调用表达式
#1
所调用std?::?ranges?::?find
,而不是std?::?find
,尽管从返回(a)所述迭代器类型begin(vec)
和end(vec)
可以关联namespace std
和(b)std?::?find
是更特化的([temp.func.order])比std?::?ranges?::?find
,因为前者需要其前两个参数具有相同的类型。
上面的示例关闭了ADL,因此调用直接转到std::ranges::find
.
让我们创建一个小例子来进一步探索:
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;
}
Run Code Online (Sandbox Code Playgroud)
来自cppreference 的描述:
本页描述的类函数实体是 niebloids,即:
- 调用其中任何一个时,不能指定显式模板参数列表。
- 对于依赖参数的查找,它们都不可见。
- 当通过对函数调用运算符左侧名称的正常非限定查找找到其中之一时,它会禁止依赖于参数的查找。
Niebloids 对参数依赖查找 (ADL) 不可见,因为它们是函数对象,并且 ADL 仅针对自由函数而不是函数对象执行。第三点是标准示例中发生的事情:
find(begin(vec), end(vec), 2); //unqualified call to find
Run Code Online (Sandbox Code Playgroud)
对 的调用find()
是不合格的,因此当查找开始时,它会找到std::ranges::find
函数对象,从而阻止 ADL 的发生。
搜索更多,我发现这是对 niebloids 和 CPO(自定义点对象)最容易理解的解释:
... CPO是一个对象(不是函数);它是可调用的;它是 constexpr 可构造的,[...] 它是可定制的(这就是“与程序定义的类型交互”的意思);而且它是受概念约束的。
[...]
如果您从上面删除形容词“可定制的、概念受限的”,那么您就有了一个关闭 ADL 的函数对象——但不一定是一个定制点。C++2a Ranges 算法,比如std::ranges::find
,就是这样的。任何可调用的、constexpr 可构造的对象通俗地称为“niebloid”,以纪念 Eric Niebler。