临时对象最初是const吗?

12

这段代码是否有未定义行为(UB)?

struct A
{
 void nonconst() {}
};

const A& a = A{};
const_cast<A&>(a).nonconst();

换句话说,这个(临时的)对象最初是const吗?我查了标准但没找到答案,所以希望引用相关部分。

编辑: 对于那些说 A{} 不是 const 的人,那么你能进行 A{}.nonconst() 吗?


2
A{} 创建的临时对象本身并不是 const。您只是创建了一个 const 引用,并将其强制转换为非 const,然后访问该临时对象。这并不会改变临时对象本身的 const 属性。如果您确定指针/引用所指向的对象实际上不是 const,则从指针/引用中强制转换 const 属性并不会导致未定义行为。如果对象实际上是 const,例如 const A a; const A &ref = a; const_cast<A&>(ref).nonconst();,那么这样做就会导致未定义行为。 - Remy Lebeau
3
你甚至可以将其绑定到 A&& - Jarod42
2
我认为它并不真正被称为临时的,因为在这种情况下,const引用会延长生命周期。 - Alex Guteniev
2
针对您的编辑,是的 - NathanOliver
1
@RemyLebeau “A{}”所创建的临时对象本身并不是const。在C++17中,“A{}”甚至不会创建临时对象。 - Language Lawyer
显示剩余3条评论
2个回答

9
参考[dcl.init.ref]/5(我加粗)中的定义,引用变量a的初始化如下:
如果初始化表达式
  • 是一个纯右值(但不是位域)[…]
则第一种情况下初始化表达式的值,第二种情况下转换的结果称为转换后的初始化器。如果转换后的初始化器是一个纯右值,则它的类型T4将调整为类型“cv1 T4”([conv.qual]),并应用临时材料化转换([conv.rval])。
因此,这意味着初始化引用的纯右值表达式A{}的类型被调整为const A
然后参考[conv.rval],其规定如下:
类型为T的纯右值可以转换为类型为T的xvalue。该转换初始化了类型为T的临时对象([class.temporary])。
因此,绑定到引用的临时对象的类型与调整后的prvalue类型相同:const A
所以代码const_cast<A&>(a).nonconst();未定义的行为

1
因此,代码const_cast<A&>(a).nonconst(); 是未定义行为。我曾经认为UB是修改常量对象。但这里没有修改。 - Language Lawyer
@LanguageLawyer True。我认为上面的代码只是一个示例,而不是生产代码。因此,我没有考虑nonconst函数体的含义。 - Oliv

3

临时变量的类型与您声明的类型相同。

不幸的是,正如Oliv他们的答案中指出的那样,引用初始化规则会将类型转换为匹配引用类型的类型,因此在这种情况下,a实际上是一个const A。它基本上做了以下操作:

using const_A = const A;
const A& a = const_A{};

因为您可以实际创建常量 prvalue,如果您想要停止重载集接受常量 prvalue,则需要以下操作:

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{});
}

在这里,由于 c_bar {} 实际上是 const ,而不是使用 foo(bar {}); 会得到一个删除函数错误,因此调用了 void foo(bar const&)。 添加
void foo(bar const&&) = delete;

需要实际停止foo(c_bar{});的编译。

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