const 在 std::views 中是否失效?

28
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;
}

为什么如果函数参数的类型是std::view,左值引用的const性质会被完全忽略?
编辑:
如果像您在评论中所写的那样,const view 引用在这个上下文中类似于 const 指针,为什么如果同一个函数以 rvalue 对象构造的视图作为参数,代码无法编译?
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;
}

6
我认为这与 const std::span<int>std::span<const int> 没有区别。 - Chris_F
4
这是同样的原因,为什么如果你有一个 int *p;,对它的 const 引用仍然允许你修改 *p - Sam Varshavchik
1
const 是浅层的,它不是传递性的。(我希望有一个传递性的 deep_const。) - Eljay
@Eljay相关:对于指针和类似指针的对象,有实验性质的“propagate_const”propagate_const - starball
标准视角完全崩溃。 - Evg
标准观点完全崩溃了。 - undefined
1个回答

28

尽管名称为Views,但它们不一定是不可修改的。通常情况下,表示包含对象序列的类如何传播const取决于使用了哪个类。这就是事情变得奇怪的地方。

看到了吗,views::all(e)的返回类型根据e的确切类型而更改。如果e是glvalue(并且本身不是视图),则返回指向由e表示的范围的ref_viewref_view的行为就像存储指向给定范围的指针一样。当然,如果您有一个类的R*成员,则其const等效项是R * const,而不是R const*。因此,ref_view无法将const传播到包含的范围中。

然而,如果e是一个prvalue(并且不是一个view),那么all返回的是一个owning_view这个对象实际上以成员变量的形式存储了e的一个拷贝(或者说从中移动)。这意味着当你得到一个const owning_view时,const会被传播到该成员变量。由于const vector<T>也传播了const,所以views::all(e)针对一个vector的prvalue也会传播const


4
我原本想保持沉默,但是... "owning_view" 不是原始设计的一部分。以前,你不能在临时容器上使用适配器。"owning_view" 的存在使设计变得混乱,并增加了这种困惑。我很遗憾它被添加进来了。 - Eric Niebler
2
一旦我们使用了views::single,设计就变得混乱了。owning_view只是一种更安全、更高效的拼写方式,可以代替views::single | views::join - T.C.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接