临时对象最初是 const 吗?
Posted
技术标签:
【中文标题】临时对象最初是 const 吗?【英文标题】:Is temporary object originally const? 【发布时间】:2019-01-23 20:06:01 【问题描述】:这个代码是 UB 吗?
struct A
void nonconst()
;
const A& a = A;
const_cast<A&>(a).nonconst();
换句话说,(临时)对象最初是const
吗?我浏览了标准,但找不到答案,因此希望能引用相关部分的内容。
编辑:对于那些说A
不是const
的人,那么你能做到A.nonconst()
吗?
【问题讨论】:
A
创建的临时本身不是 const。您只是在创建一个对它的 const 引用,然后您将丢弃该 const-ness 然后访问临时。这不会改变临时本身的 const-ness。如果您确定被指向/引用的对象实际上不是 const,那么从指针/引用中抛弃 const 不是 UB。如果对象实际上是 const,它会是 UB,例如 const A a; const A &ref = a; const_cast<A&>(ref).nonconst();
您甚至可以将其绑定到A&&
。
我不认为它真的被称为临时的,因为在这种情况下 const 引用会延长生命周期。
回复您的编辑,yes
@RemyLebeau A
创建的临时对象本身不是 const。 在 C++17 中,A
甚至不创建临时对象。跨度>
【参考方案1】:
引用a
的初始化由[dcl.init.ref]/5给出(我的粗体):
否则,如果初始化表达式
是一个右值(但不是位域)[...]那么在第一种情况下的初始化表达式的值和在第二种情况下的转换结果称为转换后的初始化程序。 如果转换后的初始值设定项是纯右值,则将其类型 T4 调整为类型“cv1 T4” ([conv.qual]) 并应用临时实现转换 ([conv.rval])。 p>
也就是说初始化引用的类型纯右值表达式A
调整为const A
。
然后[conv.rval] 状态:
T 类型的纯右值可以转换为 T 类型的 xvalue。 此转换初始化 T 类型的临时对象 ([class.temporary])。
所以临时对象的类型,绑定到引用上和调整后的prvalue
类型一样:const A
。
所以代码const_cast<A&>(a).nonconst();
是未定义的行为。
【讨论】:
所以代码 const_cast(a).nonconst();是未定义的行为 我认为UB 是修改一个const 对象。这里没有修改。 @LanguageLawyer 是的,我认为上面的代码是一个说明,而不是生产代码。所以我不认为非常量的主体是有意义的。【参考方案2】:临时的类型是你声明它的任何类型。
不幸的是,正如Oliv 在their answer 中指出的那样,引用初始化规则将类型转换为匹配引用类型,因此在这种情况下a
实际上是指const A
。基本上是在做
using const_A = const A;
const A& a = const_A;
因为如果您想阻止重载集接受您需要的常量纯右值,您实际上可以创建常量纯右值
ret_type function_name(some_type const&&) = delete;
如果有的话
ret_type function_name(some_type const&)
在重载中设置它,然后如果你只删除常量prvalue就会绑定到它
ret_type function_name(some_type&&)
相反。你可以看到这个使用
struct bar;
void foo(bar const&) std::cout << "void foo(bar const&)\n";
void foo(bar&&) =delete;
using c_bar = const bar;
int main()
foo(c_bar);
这里,void foo(bar const&)
被调用,因为 c_bar
实际上是 const
,而不是如果您使用了 foo(bar);
,则会收到已删除的函数错误。添加
void foo(bar const&&) = delete;
需要真正停止 foo(c_bar);
的编译。
【讨论】:
以上是关于临时对象最初是 const 吗?的主要内容,如果未能解决你的问题,请参考以下文章