为什么 as_const 禁止右值参数?
Why does as_const forbid rvalue arguments?
我想问为什么 as_const
禁止右值参数,according to cppreference.com(即为什么标准人员这样做,而不是为什么 cppreference.com 特别引用它们那。而且也没有在规范中将委员会的意图编纂成文,只是为了确保 :)))。这个(人工)示例会产生错误(用户希望将其设为 const 以保持 COW 安静)
QChar c = as_const(getQString())[0];
指出,如果我们 只是 删除右值引用重载的删除,它会默默地将右值转换为左值。是的,但为什么不优雅地处理右值和 return 右值输入的 const 右值和左值输入的 const 左值?
一个原因可能是由于缺乏所有权转移,它可能对右值造成危险
for (auto const &&value : as_const(getQString())) // whoops!
{
}
并且可能没有令人信服的用例来证明忽略这种可能性是合理的。
因为 as_const 不将参数作为 const 引用。
非常量左值引用不能绑定到临时对象。
转发引用重载已明确删除。
问题是处理生命周期延长
const auto& s = as_const(getQString()); // Create dangling pointer
QChar c = s[0]; // UB :-/
一种可能是以下重载(而不是删除的重载)
template< typename T >
const T as_const(T&& t) noexcept(noexcept(T(std::forward<T>(t))))
{
return std::forward<T>(t);
}
这涉及额外的构造,可能还有其他陷阱。
(我不小心回答了这个问答的错误问题 to a related question,因为我看错了;我把我的答案移到这个问题上,我的回答实际上解决了这个问题)
P0007R1 introduced std::as_const
as part of C++17. The accepted proposal did not mention rvalues at all, but the previous revision of it, P0007R0,包含关于右值的结束讨论[强调我的]:
IX. Further Discussion
The above implementation only supports safely re-casting an l-value as
const (even if it may have already been const). It is probably
desirable to have xvalues and prvalues also be usable with as_const,
but there are some issues to consider.
[...]
An alternative implementation which would support all of the forms
used above, would be:
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;
}
We believe that such an implementation helps to deal with lifetime
extension issues for temporaries which are captured by as_const, but
we have not fully examined all of the implications of these forms. We
are open to expanding the scope of this proposal, but we feel that
the utility of a simple-to-use as_const is sufficient even without the
expanded semantics.
所以 std::as_const
基本上只为左值添加,因为原始提案没有完全检查对右值实现它的含义,即使 return 按值重载右值参数是在访问最少。另一方面,最终提案侧重于为左值的常见用例获取实用程序。
P2012R0旨在解决基于范围的for循环的隐患
Fix the range‐based for loop, Rev0
The range-based for loop became the most important control structure
of modern C++. It is the loop to deal with all elements of a
container/collection/range.
However, due to the way it is currently defined, it can easily
introduce lifetime problems in non-trivial but simple applications
implemented by ordinary application programmers.
[...]
The symptom
Consider the following code examples when iterating over elements of
an element of a collection:
std::vector<std::string> createStrings(); // forward declaration
…
for (std::string s : createStrings()) … // OK
for (char c : createStrings().at(0)) … // UB (fatal runtime error)
While iterating over a temporary return value works fine, iterating
over a reference to a temporary return value is undefined behavior.
[...]
The Root Cause for the problem
The reason for the undefined behavior above is that according to the
current specification, the range-base for loop internally is expanded
to multiple statements: [...]
And the following call of the loop:
for (int i : createOptInts().value()) … // UB (fatal runtime error)
is defined as equivalent to the following:
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;
…
}
By rule, all temporary values created during the initialization of the
reference rg
that are not directly bound to it are destroyed before
the raw for loop starts.
[...]
Severity of the problem
[...]
As another example for restrictions caused by this problem consider
using std::as_const()
in a range-based for loop:
std::vector vec; for (auto&& val : std::as_const(getVector())) {
… }
Both std::ranges
with operator |
and std::as_const()
have a
deleted overload for rvalues to disable this and similar uses. With
the proposed fix things like that could be possible. We can definitely
discuss the usability of such examples, but it seems that there are
more example than we thought where the problem causes to =delete
function calls for rvalues.
这些陷阱是避免允许 std::as_const()
右值重载的一个论据,但如果 P2012R0 被接受,则可以说可以添加这样的重载(如果有人提出建议并显示了一个有效的用例) ).
我想问为什么 as_const
禁止右值参数,according to cppreference.com(即为什么标准人员这样做,而不是为什么 cppreference.com 特别引用它们那。而且也没有在规范中将委员会的意图编纂成文,只是为了确保 :)))。这个(人工)示例会产生错误(用户希望将其设为 const 以保持 COW 安静)
QChar c = as_const(getQString())[0];
一个原因可能是由于缺乏所有权转移,它可能对右值造成危险
for (auto const &&value : as_const(getQString())) // whoops!
{
}
并且可能没有令人信服的用例来证明忽略这种可能性是合理的。
因为 as_const 不将参数作为 const 引用。 非常量左值引用不能绑定到临时对象。
转发引用重载已明确删除。
问题是处理生命周期延长
const auto& s = as_const(getQString()); // Create dangling pointer
QChar c = s[0]; // UB :-/
一种可能是以下重载(而不是删除的重载)
template< typename T >
const T as_const(T&& t) noexcept(noexcept(T(std::forward<T>(t))))
{
return std::forward<T>(t);
}
这涉及额外的构造,可能还有其他陷阱。
(我不小心回答了这个问答的错误问题 to a related question,因为我看错了;我把我的答案移到这个问题上,我的回答实际上解决了这个问题)
P0007R1 introduced std::as_const
as part of C++17. The accepted proposal did not mention rvalues at all, but the previous revision of it, P0007R0,包含关于右值的结束讨论[强调我的]:
IX. Further Discussion
The above implementation only supports safely re-casting an l-value as const (even if it may have already been const). It is probably desirable to have xvalues and prvalues also be usable with as_const, but there are some issues to consider.
[...]
An alternative implementation which would support all of the forms used above, would be:
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; }
We believe that such an implementation helps to deal with lifetime extension issues for temporaries which are captured by as_const, but we have not fully examined all of the implications of these forms. We are open to expanding the scope of this proposal, but we feel that the utility of a simple-to-use as_const is sufficient even without the expanded semantics.
所以 std::as_const
基本上只为左值添加,因为原始提案没有完全检查对右值实现它的含义,即使 return 按值重载右值参数是在访问最少。另一方面,最终提案侧重于为左值的常见用例获取实用程序。
P2012R0旨在解决基于范围的for循环的隐患
Fix the range‐based for loop, Rev0
The range-based for loop became the most important control structure of modern C++. It is the loop to deal with all elements of a container/collection/range.
However, due to the way it is currently defined, it can easily introduce lifetime problems in non-trivial but simple applications implemented by ordinary application programmers.
[...]
The symptom
Consider the following code examples when iterating over elements of an element of a collection:
std::vector<std::string> createStrings(); // forward declaration … for (std::string s : createStrings()) … // OK for (char c : createStrings().at(0)) … // UB (fatal runtime error)
While iterating over a temporary return value works fine, iterating over a reference to a temporary return value is undefined behavior.
[...]
The Root Cause for the problem
The reason for the undefined behavior above is that according to the current specification, the range-base for loop internally is expanded to multiple statements: [...]
And the following call of the loop:
for (int i : createOptInts().value()) … // UB (fatal runtime error)
is defined as equivalent to the following:
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; … }
By rule, all temporary values created during the initialization of the reference
rg
that are not directly bound to it are destroyed before the raw for loop starts.[...]
Severity of the problem
[...]
As another example for restrictions caused by this problem consider using
std::as_const()
in a range-based for loop:std::vector vec; for (auto&& val : std::as_const(getVector())) { … }
Both
std::ranges
withoperator |
andstd::as_const()
have a deleted overload for rvalues to disable this and similar uses. With the proposed fix things like that could be possible. We can definitely discuss the usability of such examples, but it seems that there are more example than we thought where the problem causes to=delete
function calls for rvalues.
这些陷阱是避免允许 std::as_const()
右值重载的一个论据,但如果 P2012R0 被接受,则可以说可以添加这样的重载(如果有人提出建议并显示了一个有效的用例) ).