diff --git a/common/main/cast_range_result.h b/common/main/cast_range_result.h index 4c0c079d2..038b75111 100644 --- a/common/main/cast_range_result.h +++ b/common/main/cast_range_result.h @@ -11,9 +11,28 @@ #include "partial_range.h" template +/* Declare the requirements on the class itself, rather than the one method which needs them, because: + * - the class is useless if that one method fails its requirements + * - gcc produces better error messages if the class is rejected, than if the + * class is accepted and `operator*()` is rejected. + */ +requires( + requires(iterator_type i) { + /* If the base type's result cannot be converted to this type's result, + * then `operator*()` will raise an error when it attempts an implicit + * conversion. + */ + {*i} -> std::convertible_to; + /* If `result_type` and the base type's `operator*` are the same, then + * this adapter has no effect. + */ + requires(!std::is_same::value); + } +) class iterator_result_converter : public iterator_type { public: + constexpr iterator_result_converter() = default; iterator_result_converter(iterator_type &&iter) : iterator_type(std::move(iter)) { @@ -22,6 +41,10 @@ public: { return *this; } + /* `using iterator_type::operator*()` is not viable here because this + * `operator*()` has a different return type than + * `iterator_type::operator*()`. + */ result_type operator*() const { return this->iterator_type::operator*(); @@ -30,6 +53,12 @@ public: { return static_cast(this->iterator_type::operator++()); } + iterator_result_converter operator++(int) + { + auto r = *this; + this->iterator_type::operator++(); + return r; + } }; template ()))>>