const 是否被 std::views 破坏了?

Ale*_*104 28 c++ constants c++20 std-ranges

void foo(const auto& collection)
{
    *collection.begin() = 104;
}

int main()
{
    std::vector<int> ints {1, 2, 3, 4, 5};
    foo(ints); // Error, as it should be
    foo(ints | std::views::all); // Compiles and modifies the vector. Why?
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

如果函数的参数是类型,为什么左值引用的常量性会被完全忽略std::view

编辑:
如果,正如您在注释中所写,const 视图引用与此上下文中的 const 指针类似,那么如果同一函数采用从右值对象构造的视图作为参数,为什么代码无法编译?

std::vector<int> getVec()
{
    return std::vector{1, 2, 3, 4, 5};
}

void foo(const auto& collection)
{
    *collection.begin() = 104; // Error: assignment of read-only location
}

int main()
{
    foo(getVec() | std::views::all); // Nope!
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Nic*_*las 29

尽管有名称,视图不一定是不可修改的。一般来说,表示一系列包含对象的类如何传播const取决于使用什么类。这就是事情变得奇怪的地方。

看,返回类型的views::all(e) 变化取决于 到底e什么。如果e是一个泛左值(并且本身不是视图),那么它返回ref_view表示的范围的a eref_view其行为就好像它存储了一个指向给定范围的指针。当然,如果你有R*一个类的成员,const那么相当于R * const, not R const*。因此ref_view无法传播const到包含的范围。

但是,如果e是纯右值(并且也不是视图),那么all返回的是owning_view. 这个对象实际上存储了一个副本e好吧,它从它移动)作为成员。这意味着当您获得 a 时const owning_view,它const会传播到该成员。由于const vector<T> 传播const,所以 aviews::all(e)的纯右值也会传播vector

  • 我本想保持沉默,但……“owning_view”并不是原始设计的一部分。以前,您无法在临时容器上使用适配器。“owning_view”的存在使设计变得混乱并加剧了这种混乱。我很遗憾它被添加了。 (4认同)
  • 一旦我们得到“views::single”,设计就变得混乱了。“owning_view”只是拼写“views::single |”的一种更安全、更有效的方式。视图::加入`。 (2认同)