1. ホーム
  2. c++

[解決済み] ニエブロイドとは?

2023-04-12 01:34:50

質問

C++20では、cppreferenceでquot;niebloid"という用語をより頻繁に読むことができるようになりました。

SOでは、今日2020/07/16に2つの記事がそれに言及しているのが見つかりました。

Googleもそれほど多くの結果を吐き出すわけではありません。最も顕著なのは、おそらく ここで .

誰かニーブロイドにもう少し光を当ててくれませんか?

どのように解決するのですか?

用語 ニエブロイド の由来は エリック・ニーブラー(Eric Niebler)の という名前からきています。簡単に言うと、ADL(引数依存参照)を無効にする関数オブジェクトで、これにより std:: のオーバーロードが拾われないようにするための関数オブジェクトです。 std::ranges のアルゴリズムが呼び出されたときに拾われない。

のツイート(2018年)と回答はこちらです。 エリック 自身 という名前にしました。エリックが書いた 記事 を2014年に書き、このコンセプトを説明しています。

で最もよく見ることができます。 規格の文書そのものです。 :

25.2.2

で定義されたエンティティは std​::​ranges の名前空間は、引数依存の名前検索では見つかりません ( 基本.lookup.argdep ). 非限定型( 基本.lookup.unqual ) 名前検索で見つかった場合、それらは引数依存の名前検索を抑制します。

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 というのは、前者は最初の2つのパラメータが同じ型であることを必要とするからです。

上記の例では 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;
}

説明文は cppreference :

このページで説明されている機能的な実体は、ニーブロイドである、ということです。

  • 明示的なテンプレート引数リストは、それらのいずれかを呼び出す際に指定してはならない。
  • どれも引数依存のルックアップでは見えない。
  • それらのいずれかが、関数呼び出し演算子の左側にある名前の通常の非限定検索によって見つかった場合、引数依存の検索を抑制します。

ニーブロイドは関数オブジェクトであるため、引数依存参照(ADL)では見えません。3つ目のポイントは、標準の例で起こったことです。

find(begin(vec), end(vec), 2); //unqualified call to find

の呼び出しは find() の呼び出しは修飾されていないので、ルックアップが始まると std::ranges::find 関数オブジェクトを見つけ、それがADLの発生を止めることになります。

さらに検索してみると この が、ニーブロイドとCPO(カスタマイズ ポイント オブジェクト)についての最もわかりやすい説明だと思います。

... a CPO はオブジェクト(関数ではない)であり、呼び出し可能であり、constexpr-constructable であり、[...] カスタマイズ可能であり(これが「プログラム定義型と相互作用する」という意味です)、概念制約付きであることです。

[...]

上記から「カスタマイズ可能、概念に縛られる」という形容詞を取り除くと、ADLをオフにする機能オブジェクトができます - しかし、必ずしもカスタマイズのポイントではありません . C++2a Ranges アルゴリズムのような std::ranges::find は,このようなものである. 呼び出し可能で constexpr で構成可能なオブジェクトは、俗に "ニーブロ" と呼ばれます。 Eric Nieblerに敬意を表して。