Niebloid とは?
Niebloid とは、 std::ranges
の元になったライブラリ range-v3 の作者である Eric Niebler さんの名前をもじった、C++ における「ADL(argument dependent lookup)を引き起こさない関数テンプレートのような何か」を指す言葉です。
この niebloid という概念は C++20 にて暗黙的に導入され、 C++26 で廃止されることになりました。一体何があったのでしょうか。
Niebloid の導入
C++20 では <algorithm>
ヘッダに既存のアルゴリズムと同名のものが std::ranges
名前空間以下に追加されました。
namespace std {
template <class I>
auto reverse(I, I); // #1
namespace ranges {
template <class I, class S>
auto reverse(I, S); // #2
template <class R>
auto reverse(R&&);
} // namespace ranges
} // namespace std
このとき以下のような呼び出しを行うと、#1 と #2 のいったいどちらが呼び出されるのでしょうか。
template <class R>
void func(R&& r) {
using namespace std::ranges;
reverse(begin(r), end(r)); // std::ranges::reverse を呼んでほしい
}
もし begin(r)
や end(r)
の型が std
名前空間になければ、見えている関数は std::ranges::reverse
だけなのでそちらが呼び出されますが、std
名前空間に含まれている場合は ADL によって std::reverse
も候補に入り、もし2つの型が同じならば #1 の関数が優先されてしまい、これは意図通りではありません。
もしここで reverse
が関数オブジェクトとして実装されていたとすれば、 ADL は関数や関数テンプレートに対してしか働かないので問題は解決しますが、かといって C++ 標準規格としては今まで通り関数テンプレートとしてアルゴリズムを規定したいという思いがあり、そこで niebloid が導入されました。(明示的に niebloid という語が規格に導入されたわけではありませんが)
Niebloid の廃止
こうして導入された niebloid ですが、各処理系がそのような特殊な機能を用意したわけではなく、結局のところ関数オブジェクトとして実装されていました。1
そして関数オブジェクトではないかもしれないことによるデメリットとして、関数オブジェクトを引数に取る関数には niebloid を直に渡せないことがあります。
// std::ranges::distance は niebloid なので…
// 動かない「かも」しれないコード
auto x = std::views::transform(r, std::ranges::distance);
// 「正しい」呼び出し
auto y = std::views::transform(r, [](auto&& v) { return std::ranges::distance(v); });
これらのことから標準化委員会も niebloid とするデメリットの方が大きいと考えたようで、C++26 では前述のアルゴリズムたちを関数オブジェクトとすることになりました。2
あとがき
多くのユーザーにとってこの変更は気づかないほど些細なものです。
しかし C++26 からは std::ranges
以下の関数群を気兼ねなく関数オブジェクトとして引数に投げられますね。
嬉しい!
参照
-
MSVC では niebloid をわざわざコピー・ムーブ不可としてオブジェクトとしての使用を禁じていました ↩