什么是尼布洛德?

Arm*_*gny 75 c++ c++20

使用 C++20,我们现在可以在 cppreference 中更频繁地阅读术语“niebloid”。

在 SO 上,我们今天可以找到 2020/07/16 2 篇提到它的文章:

谷歌也没有吐出那么多结果。最突出的也许是这里

有人可以更多地了解 niebloids 吗?

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)名称查找找到后缀表达式时,它们会禁止依赖于参数的名称查找。

void foo() {
  using namespace std::ranges;
  std::vector<int> vec{1,2,3};
  find(begin(vec), end(vec), 2);        // #1
}
Run Code Online (Sandbox Code Playgroud)

在函数调用表达式#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。

  • 我建议“niebloid”,舌头紧紧地贴在脸颊上。令我懊恼的是,它卡住了。对我来说是对的。 (41认同)
  • 当我读到这样的帖子时,我意识到我有多少不知道。 (21认同)
  • Niebloids_不_保证是对象。它们被指定为神奇的函数模板,并带有足够的狡猾措辞,以允许它们作为对象实现,但仅此而已。 (6认同)
  • 该民意调查中的“ADL 免疫功能”似乎是一个更好的、自我描述性的名称。为什么不使用它来代替? (3认同)