Portably supporting relaxed range-for-loop

139 views Asked by At

C++17 will (probably) relax the definition of the range for loop, allowing end() to return a distinct type (e.g. a sentinel):

struct MyRange {
    struct Sentinel {};
    int* begin();
    Sentinel end();
};
bool operator!=(int*, MyRange::Sentinel);

Currently the only compilers that support this are gcc 6.1 and clang 4.0+ (example) (example of error message). If I'm writing a range type where a sentinel would be more efficient for the end type, how can I detect whether the compiler supports relaxed range for? I can't see any discussion of this in P0184R0 (linked above); will a feature test macro be provided?

Further questions:

  • If there is a way to detect compiler support, is it safe (for the library) to change the return type of my end() member function? Do I need e.g. to make my sentinel implicitly convertible to my iterator type?
  • Conversely, for pre-C++17 compilers is it worth exposing my sentinel under a different name (e.g. sentinel())? Can pre-C++17 algorithms efficiently use [begin(), sentinel()) or is it not worth the additional code?
2

There are 2 answers

0
Nicol Bolas On BEST ANSWER

Per P0096R3, you can check whether __cpp_range_based_fo‌​r is greater than or equal to 201603. Of course, Visual Studio does not support any of the feature test macros, so you'll have to check its versioning separately. They shipped support for it in VS2015 Update 3, but along with most other C++17 support, you have to use the /std:c++latest switch.

0
Yakk - Adam Nevraumont On

The reason why sentinels are efficient rarely has to do with the internal state of the sentinel.

So one approach is to give your Sentinel enough internal state to generate the iterator version of itself.

Even in C++14 with uniform end-begin for(:) loops and algorithms that insist on identical start/finish iterator types, hand-written algorithms or loops could use your sentinel and gain efficiency.

A library like Boost.Config is usually your best bet if you want maximal coverage, but a C++17 feature-detection set has not been published as of this date (that I can find).

There is, as far as I can tell, no way to SFINAE-detect the existence of for(:) loops that accept different begin/end iterator types. (I could imagine something constexpr noexcept something hacks, but that is beyond my skill.)

Detecting if <algorithms> accept sentinel end iterators can probably be done with SFINAE. But I am uncertain even if C++17 has sentinel-ized <algorithms> or even intends to; ranges-v3 is supposed to handle the rework of that.