C++20から、<iterator>
や<algorithm>
にある一部の関数テンプレートに対して、よりジェネリックに定義し直されたものがstd::ranges
名前空間に同じ名前で追加されている。
規格書においてはこれらの関数テンプレートはADLで発見されない事を規定している。
そのうちの一つであるstd::ranges::distance
を例として上げると、次のような場合にADLを無効化して呼び出される。
void foo() {
// std::ranges名前空間を取り込む
using namespace std::ranges;
std::vector<int> vec{1,2,3};
distance(begin(vec), end(vec)); // #1
}
#1
の箇所で呼ばれる関数はstd::distance
ではなくstd::ranges::distance
となる。オーバーロードの半順序においてはより特殊化されている関数テンプレートが選択されると言うルールがあり、std::distance
の方がstd::ranges::distance
よりも特殊化されている(イテレータ型と番兵型が1つのテンプレートパラメータで指定されている)が、それとは関係なく常にstd::ranges::distance
が選択される。
これは、std::ranges
以下にあるこのような規定を持つ関数が通常の(修飾・非修飾名に対する)名前探索で見えているときは必ずそちらが選択されると言う事であり、その際はADLが行われない(無効化される)事を規定している。
std::ranges
名前空間に追加される関数テンプレート群は、同名のstd
名前空間直下にある既存のものに対してC++20の<ranges>
が規定するイテレータ/rangeコンセプトに適合するように再設計されたものであり、このような規定はC++20以降古い関数が意図せず使用されないようにするための処置であると考えられる。
この性質は通常の関数では実現できず、これらの関数テンプレートは実のところ関数テンプレートではない。おそらく関数オブジェクトとして実装されるものと思われる。以下は、関数オブジェクトによる実装例である:
#include <iostream>
#include <iterator>
#include <vector>
namespace my_range {
struct distance_t {
template <class Iterator>
auto operator()(Iterator first, Iterator last) const {
std::cout << "call my_distance" << std::endl;
int n = 0;
for (;first != last; ++first, ++n) {}
return n;
}
};
constexpr inline distance_t distance{};
}
int main() {
using namespace my_range;
std::vector<int> v = {1, 2, 3};
int n = distance(begin(v), end(v));
std::cout << n << std::endl;
}
出力:
call my_distance
3
呼称について
このように規定された関数テンプレートを指す公式の名称や広く合意された呼称はないが、これらの発明者であり提案者であるEric Niebler氏の名前からniebloidと呼ばれる事がある。