c++ - 初始化表达式可以使用变量本身吗?

标签 c++ initialization language-lawyer undefined-behavior

考虑以下代码:

#include <iostream>

struct Data
{
    int x, y;
};

Data fill(Data& data)
{
    data.x=3;
    data.y=6;
    return data;
}

int main()
{
    Data d=fill(d);
    std::cout << "x=" << d.x << ", y=" << d.y << "\n";
}

这里 d 是从 fill() 的返回值复制初始化的,但是 fill() 写入到 d 本身在返回结果之前。我担心的是 d 在被初始化之前是非平凡使用的,并且在某些(所有?)情况下使用未初始化的变量会导致未定义的行为。

那么这段代码是有效的,还是有未定义的行为?如果它是有效的,一旦 Data 停止成为 POD 或在其他情况下,行为是否会变得未定义?

最佳答案

这似乎不是有效的代码。它类似于问题中概述的情况:Is passing a C++ object into its own constructor legal? ,尽管在那种情况下代码是有效的。机制并不相同,但基本推理至少可以让我们开始。

我们从 defect report 363 开始它要求:

And if so, what is the semantics of the self-initialization of UDT? For example

 #include <stdio.h>

 struct A {
        A()           { printf("A::A() %p\n",            this);     }
        A(const A& a) { printf("A::A(const A&) %p %p\n", this, &a); }
        ~A()          { printf("A::~A() %p\n",           this);     }
 };

 int main()
 {
  A a=a;
 }

can be compiled and prints:

A::A(const A&) 0253FDD8 0253FDD8
A::~A() 0253FDD8

提议的决议是:

3.8 [basic.life] paragraph 6 indicates that the references here are valid. It's permitted to take the address of a class object before it is fully initialized, and it's permitted to pass it as an argument to a reference parameter as long as the reference can bind directly. [...]

因此,虽然 d 没有完全初始化,但我们可以将其作为引用传递。

我们开始遇到麻烦的地方在这里:

data.x=3;

C++ 标准部分草案 3.8(缺陷报告引用的相同部分和段落)说(强调我的):

Similarly, before the lifetime of an object has started but after the storage which the object will occupy has been allocated or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any glvalue that refers to the original object may be used but only in limited ways. For an object under construction or destruction, see 12.7. Otherwise, such a glvalue refers to allocated storage (3.7.4.2), and using the properties of the glvalue that do not depend on its value is well-defined. The program has undefined behavior if:

  • an lvalue-to-rvalue conversion (4.1) is applied to such a glvalue,

  • the glvalue is used to access a non-static data member or call a non-static member function of the object, or

  • the glvalue is bound to a reference to a virtual base class (8.5.3), or

  • the glvalue is used as the operand of a dynamic_cast (5.2.7) or as the operand of typeid.

那么访问是什么意思呢? defect report 1531 澄清了这一点将访问定义为:

access

to read or modify the value of an object

所以 fill 访问一个非静态数据成员,因此我们有未定义的行为。

这也与 12.7 部分一致,其中说:

[...]To form a pointer to (or access the value of) a direct non-static member of an object obj, the construction of obj shall have started and its destruction shall not have completed, otherwise the computation of the pointer value (or accessing the member value) results in undefined behavior.

由于您无论如何都在使用拷贝,因此您不妨在 fill 内创建一个 Data 实例并对其进行初始化。您避免必须通过 d.

正如 T.C. 指出的那样。明确引用生命周期何时开始的细节很重要。来自 3.8 部分:

The lifetime of an object is a runtime property of the object. An object is said to have non-trivial initialization if it is of a class or aggregate type and it or one of its members is initialized by a constructor other than a trivial default constructor. [ Note: initialization by a trivial copy/move constructor is non-trivial initialization. — end note ] The lifetime of an object of type T begins when:

  • storage with the proper alignment and size for type T is obtained, and

  • if the object has non-trivial initialization, its initialization is complete.

初始化很重要,因为我们是通过复制构造函数进行初始化的。

关于c++ - 初始化表达式可以使用变量本身吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33649370/

相关文章:

c++ - clang++ 8.0.1 自分配重载警告

c++ - 虚拟继承与非默认构造函数

c++ - catch without try 编译成功

object - F# 中的内联 C# 对象创建

ios - 我应该在哪里准备数据?在 awakeFromNib、viewDidLoad 或其他东西中

c++ - 数组初始化知识

c++ - std::list 的多元素插入是强异常安全的吗?

c++ - 四向等价

c++ - 将我的数组移动到 Mat 并使用 openCV 显示图像

c++ - 如何检查cin中是否有任何东西[C++]