将 C++ 对象传递给它自己的构造函数是否合法? [英] Is passing a C++ object into its own constructor legal?

查看:25
本文介绍了将 C++ 对象传递给它自己的构造函数是否合法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我很惊讶地发现以下方法有效:

I am surprised to accidentally discover that the following works:

#include <iostream>            
int main(int argc, char** argv)
{
  struct Foo {
    Foo(Foo& bar) {
      std::cout << &bar << std::endl;
    }
  };
  Foo foo(foo); // I can't believe this works...
  std::cout << &foo << std::endl; // but it does...
}

我将构造对象的地址传递给它自己的构造函数.这看起来像是源代码级别的循环定义.标准是否真的允许您在构建对象之前将对象传递给函数,或者这是未定义的行为?

I am passing the address of the constructed object into its own constructor. This looks like a circular definition at the source level. Do the standards really allow you to pass an object into a function before the object is even constructed or is this undefined behavior?

我想这并不奇怪,因为所有类成员函数都已经有一个指向它们的类实例的数据的指针作为隐式参数.并且数据成员的布局在编译时是固定的.

I suppose it's not that odd given that all class member functions already have a pointer to the data for their class instance as an implicit parameter. And the layout of the data members is fixed at compile time.

注意,我不是在问这是否有用或一个好主意;我只是想了解更多关于课程的信息.

Note, I'm NOT asking if this is useful or a good idea; I'm just tinkering around to learn more about classes.

推荐答案

这不是未定义的行为.尽管 foo 未初始化,但您正在以标准允许的方式使用它.在为对象分配空间之后但在完全初始化之前,您可以有限地使用它.允许绑定对该变量的引用并获取其地址.

This is not undefined behavior. Although foo is uninitialized, you are using it a way that is allowed by the standard. After space is allocated for an object but before it is fully initialized, you are allowed to use it limited ways. Both binding a reference to that variable and taking its address are allowed.

缺陷报告 363:初始化来自 self 的类 说:

如果是这样,UDT自初始化的语义是什么?例如

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
",            this);     }
        A(const A& a) { printf("A::A(const A&) %p %p
", this, &a); }
        ~A()          { printf("A::~A() %p
",           this);     }
 };

 int main()
 {
  A a=a;
 }

可以编译打印:

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

分辨率为:

3.8 [basic.life] 第 6 段表示这里的引用是有效的.允许在完全初始化之前获取类对象的地址,并且允许将其作为参数传递给引用参数,只要该引用可以直接绑定即可.除了无法将 printf 中的 %p 的指针强制转换为 void * 之外,这些示例均符合标准.

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. Except for the failure to cast the pointers to void * for the %p in the printfs, these examples are standard-conforming.

C++14 标准草案中 3.8 [basic.life] 部分的完整引用如下:

The full quote of section 3.8 [basic.life] from the draft C++14 standard is as follows:

同样,在对象的生命周期开始之前但在对象生命周期结束之后对象将占用的存储空间已被分配,或者在对象的生命周期已经结束,并且在对象的存储之前被占用的对象被重用或释放,任何引用可以使用原始对象,但只能以有限的方式使用.对于一个对象在建或毁坏中,见 12.7.否则,这样的左值指分配的存储(3.7.4.2),并使用属性不依赖于其值的 glvalue 是明确定义的.该程序在以下情况下具有未定义的行为:

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:

  • 左值到右值的转换 (4.1) 应用于这样的泛左值,

  • 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

泛左值绑定到对虚拟基类 (8.5.3) 的引用,或

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

泛左值用作 dynamic_cast (5.2.7) 的操作数或 typeid 的操作数.

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

我们不会对 foo 做任何属于上述项目符号定义的未定义行为的事情.

We are not doing anything with foo that falls under undefined behavior as defined by the bullets above.

如果我们用 Clang 尝试这个,我们会看到一个不祥的警告(现场观看):

If we try this with Clang, we see an ominous warning (see it live):

警告:变量 'foo' 在它自己的初始化 [-Wuninitialized] 中使用时未初始化

warning: variable 'foo' is uninitialized when used within its own initialization [-Wuninitialized]

这是一个有效的警告,因为从未初始化的自动变量产生不确定的值是未定义的行为.但是,在这种情况下,您只是绑定了一个引用并在构造函数中获取变量的地址,这不会产生不确定的值并且是有效的.另一方面,以下来自 C++11 标准草案的自初始化示例:

It is a valid warning since producing an indeterminate value from an uninitialized automatic variable is undefined behavior. However, in this case you are just binding a reference and taking the address of the variable within the constructor, which does not produce an indeterminate value and is valid. On the other hand, the following self-initialization example from the draft C++11 standard:

int x = x ;

确实会调用未定义的行为.

does invoke undefined behavior.

活动问题 453:引用只能绑定到有效"对象 似乎也相关,但仍然是开放的.最初提议的语言与缺陷报告 363 一致.

Active issue 453: References may only bind to "valid" objects also seems relevant but is still open. The initial proposed language is consistent with Defect Report 363.

这篇关于将 C++ 对象传递给它自己的构造函数是否合法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆