为什么简单的迭代器不可读?

Why is a simple iterator not readable?

此代码无法使用 range-v3 0.10.0(或 master)编译。它确实用 range-v3 0.9.1.

编译
#include "range/v3/all.hpp"

struct double_it {
  using value_type = double;
  double x;
  double& operator*() { return x; }
  const double& operator*() const { return x; }
};
static_assert(ranges::readable<double_it>);

哪个版本合适?在 master 中,类型 I 仅当 same_as<iter_reference_t<I const>, iter_reference_t<I>> 时才为 readable。我不明白为什么 operator* 的 return 类型应该与 operator* const.

的类型相同

注意:问题提交于 github here

与 Ranges TS 工作草案相比,这似乎至少是 range-v3 中的一个不一致。

关于为什么 these same_as predicates were added to to the readable concept for iterators in range-v3 issue 1449.

的描述非常稀疏

Get the range library compiling with msvc 19.24.28319 with /std:c++17

...

- CPP_concept_fragment(readable_, (I),
+ CPP_concept_fragment(readable_,
+     requires (/*I const i*/) //
+     (
+         // { *i } -> same_as<iter_reference_t<I>>;
+         // { iter_move(i) } -> same_as<iter_rvalue_reference_t<I>>;
+         0
+     ) &&
+     same_as<iter_reference_t<I const>, iter_reference_t<I>> &&
+     same_as<iter_rvalue_reference_t<I const>, iter_rvalue_reference_t<I>> &&
      common_reference_with<iter_reference_t<I> &&, iter_value_t<I> &> &&
      common_reference_with<iter_reference_t<I> &&,
                            iter_rvalue_reference_t<I> &&> &&
      common_reference_with<iter_rvalue_reference_t<I> &&, iter_value_t<I> const &>
  );

概念实现中的 same_as 谓词似乎旨在实现以下要求:

// { *i } -> same_as<iter_reference_t<I>>;
// { iter_move(i) } -> same_as<iter_rvalue_reference_t<I>>;

甚至在此实施更改之前就存在(如 range/v3/iterator/concepts.hpp 中的注释)。

但是,据我所知,none 这些要求存在于生成先前链接草案的 working draft of [iterators.readable] of Ranges TS (nor in the current HEAD of ericniebler/stl2 中。

[iterators.readable] Concept Readable

The Readable concept is satisfied by types that are readable by applying operator* including pointers, smart pointers, and iterators.

template <class In>
concept bool Readable =
  requires {
    typename value_type_t<In>;
    typename reference_t<In>;
    typename rvalue_reference_t<In>;
  } &&
  CommonReference<reference_t<In>&&, value_type_t<In>&> &&
  CommonReference<reference_t<In>&&, rvalue_reference_t<In>&&> &&
  CommonReference<rvalue_reference_t<In>&&, const value_type_t<In>&>;

report this as an issue 可能是个好主意,至少要弄清楚为什么 range-v3 实现看起来与 Ranges TS 的 [iterators.readable] 不同。

看看 P1878,其中包含了这一后期设计更改的所有基本原理。迭代器代表间接。在 C++ 中,const-ness 很浅,这意味着它不遵循间接寻址。无论您取消引用 int* 还是 int*const,您得到的结果都是相同的:int&。顶级 const 不——也不应该——重要。迭代器和指针一样。他们必须这样做,因为指针是迭代器概念的有效模型。

为了使这种区别更加明确,在 C++20 中,readable 概念称为 indirectly_readable

TL;DR:不要让迭代器的引用类型依赖于迭代器本身的 const-ness。