(在误读后,我不小心回答了此问答的错误问题to a related question;我正在将我的答案转移到这个问题,我的答案实际解决的问题)
P0007R1 作为 C++17 的一部分引入了 std::as_const。被接受的提案根本没有提到右值,但它的先前修订版P0007R0 包含对右值的结束讨论[emphasis mine]:
九。进一步讨论
上述实现仅支持安全地将左值重铸为
const(即使它可能已经是 const)。大概是
希望有 xvalues 和 prvalues 也可以与 as_const 一起使用,
但有一些问题需要考虑。
[...]
支持所有形式的替代实现
上面使用的是:
template< typename T >
inline const T &
as_const( const T& t ) noexcept
{
return t;
}
template< typename T >
inline const T
as_const( T &&t ) noexcept( noexcept( T( t ) ) )
{
return t;
}
我们相信这样的实现有助于处理生命周期
as_const 捕获的临时对象的扩展问题,但是
我们还没有完全研究这些形式的所有含义。我们
对扩大本提案的范围持开放态度,但我们认为
即使没有
扩展语义。
所以std::as_const 基本上只为左值添加,因为最初的提案没有完全检查为右值实现它的含义,即使至少访问了右值参数的按值重载返回。另一方面,最终提案侧重于将实用程序用于左值的常见用例。
P2012R0旨在解决基于范围的for循环的隐患
修复基于范围的 for 循环,Rev0
基于范围的for循环成为最重要的控制结构
现代C++。它是处理 a 的所有元素的循环
容器/集合/范围。
但是,由于它当前的定义方式,它可以很容易地
在不平凡但简单的应用程序中引入生命周期问题
由普通应用程序员实现。
[...]
症状
在遍历元素时考虑以下代码示例
集合的元素:
std::vector<std::string> createStrings(); // forward declaration
…
for (std::string s : createStrings()) … // OK
for (char c : createStrings().at(0)) … // UB (fatal runtime error)
虽然迭代临时返回值可以正常工作,但 迭代
对临时返回值的引用是未定义的行为。
[...]
问题的根本原因
上述未定义行为的原因是根据
当前规范,内部循环的范围基础已扩展
多个语句:[...]
以及下面的循环调用:
for (int i : createOptInts().value()) … // UB (fatal runtime error)
定义为等价于以下内容:
auto&& rg = createOptInts().value(); // doesn’t extend lifetime of returned optional
auto pos = rg.begin();
auto end = rg.end();
for ( ; pos != end; ++pos ) {
int i = *pos;
…
}
根据规则,在初始化期间创建的所有临时值
未直接绑定到它的引用rg 在之前被销毁
原始 for 循环开始。
[...]
问题的严重性
[...]
作为由此问题引起的限制的另一个示例,请考虑
在基于范围的 for 循环中使用 std::as_const():
std::vector vec; for (auto&& val : std::as_const(getVector())) {
… }
std::ranges 和 operator | 和 std::as_const() 都有
删除了右值的重载以禁用此和类似用途。
建议的修复方法是可能的。我们绝对可以
讨论此类示例的可用性,但似乎有
比我们想象的更多的例子问题导致=delete
函数调用右值。
这些陷阱是避免允许右值使用 std::as_const() 重载的一个论据,但如果 P2012R0 被接受,则可以说可以添加这样的重载(如果有人提出建议并展示了有效的用例)。