【一人 C++20 Advent Calendar 2019】範囲 for 文がカスタマイゼーションポイントを見つけるルールを緩和【3日目】

一人 C++20 Advent Calendar 2019 3日目の記事になります。

範囲 for 文がカスタマイゼーションポイントを見つけるルールを緩和

C++17 までは範囲 for で begin / end を参照する場合に以下のような挙動になっていました。

  1. begin() / end() 両方のメンバ関数を持っていればそれを使用する
  2. begin() / end() 両方のメンバ関数を持っていなければ ADL で非メンバ関数begin() / end() を参照する
  3. begin() もしくは end() の片方のメンバ関数しか持ってない場合はエラーになる

この時に 3. の挙動を緩和し『どちらか片方が定義されていなければ非メンバ関数begin() / end() を探しに行く』という挙動になります。

#include <iostream>
#include <vector>

// オレオレ range クラス
struct range{
    // int を counting するだけの iterator
    struct counting_iterator{
        auto operator*() { return value; }
        auto operator ++() { return ++value; }
        auto operator !=(counting_iterator const& other) { return value != other.value; }
        int value;
    };
    // begin ではなくて end だけ定義されている
    auto first() const { return counting_iterator { first_ }; }
    auto end() const { return counting_iterator { end_ }; }

    int first_, end_;
};


// range-based for に対応させる為に後から begin 関数を用意する
auto
begin(range const& r) {
    return r.first();
}

auto
end(range const& r) {
    return r.end();
}

int
main(){
    auto r1_10 = range{ 1, 10 };
    // range は end() しか定義していないので C++17 ではエラーになる
    // C++20 では条件が緩和されているので動作する
    for(auto x : r1_10) {
        std::cout << x << std::endl;
    }
    return 0;
}
/*
output:
1
2
3
4
5
6
7
8
9
*/

上記の例でいえば range クラスは end() メンバ関数しか定義していないので C++17 ではエラーになりますが、C++20 では動作するようになります。
動作するようになるんですが手元で試してみたら C++17 でも動作しました。
参照しているサイトでも C++20 からって書いてあるんですがどういうことだろうか…C++ なにもわからない…

サポートコンパイラ?

参照