【问题标题】:Is taking the address of a member of an uninitialized object well defined?获取未初始化对象成员的地址是否定义明确?
【发布时间】:2018-04-25 18:22:06
【问题描述】:

考虑以下示例。当bar被构造时,它给它的基类型(foo)构造函数my_member.y的地址,其中my_member是尚未初始化的数据成员。

struct foo {
    foo(int * p_x) : x(p_x) {}
    int * x;
};

struct member {
    member(int p_y) : y(p_y) {}
    int y;
};

struct bar : foo
{
    bar() : foo(&my_member.y), my_member(42) {}
    member my_member;
};

#include <iostream>

int main()
{
    bar my_bar;
    std::cout << *my_bar.x;
}

这是明确定义的吗?获取未初始化对象数据成员的地址是否合法?我发现this question 关于将引用传递给未初始化的对象,但这并不完全相同。在这种情况下,我在一个未初始化的对象上使用了member access operator.

确实,对象的数据成员的地址不应该通过初始化来改变,但这并不一定会使获取该地址得到很好的定义。此外,member access operators 上的 ccpreference.com 页面有这样的说法:

两个运算符的第一个操作数即使没有必要也会被计算(例如,当第二个操作数命名为静态成员时)。

我理解这意味着在&amp;my_member.y 的情况下my_member 将被评估,我认为这很好(就像int x; x; 似乎很好)但我也找不到支持它的文档。

【问题讨论】:

  • 这绝对是很好的定义。并且算子的内置地址不访问成员存储。给定的引用仅指 .-&gt; 运算符。
  • @VTT 然后我想我要问的是在&amp;my_member.y 中使用. 是否定义明确。
  • 哦,所以我猜&amp;my_member.y 的问题实际上不是运算符的地址,而是评估仍未初始化的对象的成员访问运算符。我应该更仔细地阅读问题。

标签: c++ language-lawyer object-lifetime ctor-initializer member-access


【解决方案1】:

首先让我们准确地回答问题。

您所做的不是使用未初始化的对象,而是使用不在其生命周期内的对象。 my_member 是在foo 之后构造的,因此my_member 的生命周期还没有在foo(&amp;my_member.y) 中开始。

来自[basic.life]

在对象的生命周期开始之前但在分配对象将占用的存储空间之后[...],可以使用任何引用原始对象的glvalue,但只能以有限的方式使用。 [...] 这样的 glvalue 指的是分配的存储,并且使用不依赖于其值的 glvalue 的属性是明确定义的。如果出现以下情况,则程序具有未定义的行为:

  • glvalue 用于访问对象,或 [...]

这里访问它意味着读取或修改对象的值。

my_member 的求值产生一个左值,没有什么需要转换为纯右值,因此它保持一个左值。同样,my_member.y 的评估也是一个左值。然后我们得出结论,没有对象的值被访问过,这是明确定义的。

【讨论】:

    【解决方案2】:

    是的,您可以将&amp;my_member.y 传递给foo 的构造函数,甚至可以复制指针——您可以使用x(p_x)

    虽然在foo 的构造函数中取消引用该指针的行为是未定义的。 (但你不这样做。)

    【讨论】:

      猜你喜欢
      • 2019-09-28
      • 2015-04-19
      • 2011-03-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-09
      • 2017-09-20
      • 2012-10-24
      相关资源
      最近更新 更多