問(wèn)題描述
我很驚訝地發(fā)現(xiàn)以下方法有效:
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...
}
我將構(gòu)造對(duì)象的地址傳遞給它自己的構(gòu)造函數(shù).這看起來(lái)像是源代碼級(jí)別的循環(huán)定義.標(biāo)準(zhǔn)是否真的允許您在構(gòu)建對(duì)象之前將對(duì)象傳遞給函數(shù),或者這是未定義的行為?
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?
我想這并不奇怪,因?yàn)樗蓄?lèi)成員函數(shù)都已經(jīng)有一個(gè)指向它們的類(lèi)實(shí)例的數(shù)據(jù)的指針作為隱式參數(shù).并且數(shù)據(jù)成員的布局在編譯時(shí)是固定的.
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.
注意,我不是在問(wèn)這是否有用或一個(gè)好主意;我只是想了解更多關(guān)于課程的信息.
Note, I'm NOT asking if this is useful or a good idea; I'm just tinkering around to learn more about classes.
推薦答案
這不是未定義的行為.盡管 foo
未初始化,但您正在以標(biāo)準(zhǔn)允許的方式使用它.在為對(duì)象分配空間之后但在完全初始化之前,您可以有限地使用它.允許綁定對(duì)該變量的引用并獲取其地址.
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.
缺陷報(bào)告 363:初始化來(lái)自 self 的類(lèi) 說(shuō):
如果是這樣,UDT自初始化的語(yǔ)義是什么?例如
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 段表示這里的引用是有效的.允許在完全初始化之前獲取類(lèi)對(duì)象的地址,并且允許將其作為參數(shù)傳遞給引用參數(shù),只要該引用可以直接綁定即可.除了無(wú)法將 printf 中的 %p 的指針強(qiáng)制轉(zhuǎn)換為 void * 之外,這些示例均符合標(biāo)準(zhǔn).
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 標(biāo)準(zhǔn)草案中 3.8
[basic.life] 部分的完整引用如下:
The full quote of section 3.8
[basic.life] from the draft C++14 standard is as follows:
同樣,在對(duì)象的生命周期開(kāi)始之前但在對(duì)象生命周期結(jié)束之后對(duì)象將占用的存儲(chǔ)空間已被分配,或者在對(duì)象的生命周期已經(jīng)結(jié)束,并且在對(duì)象的存儲(chǔ)之前被占用的對(duì)象被重用或釋放,任何引用可以使用原始對(duì)象,但只能以有限的方式使用.對(duì)于一個(gè)對(duì)象在建或毀壞中,見(jiàn) 12.7.否則,這樣的左值指分配的存儲(chǔ)(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:
左值到右值的轉(zhuǎn)換 (4.1) 應(yīng)用于這樣的泛左值,
an lvalue-to-rvalue conversion (4.1) is applied to such a glvalue,
泛左值用于訪問(wèn)非靜態(tài)數(shù)據(jù)成員或調(diào)用非靜態(tài)成員函數(shù)對(duì)象,或
the glvalue is used to access a non-static data member or call a non-static member function of the object, or
泛左值綁定到對(duì)虛擬基類(lèi) (8.5.3) 的引用,或
the glvalue is bound to a reference to a virtual base class (8.5.3), or
泛左值用作 dynamic_cast (5.2.7) 的操作數(shù)或 typeid 的操作數(shù).
the glvalue is used as the operand of a dynamic_cast (5.2.7) or as the operand of typeid.
我們不會(huì)對(duì) foo
做任何屬于上述項(xiàng)目符號(hào)定義的未定義行為的事情.
We are not doing anything with foo
that falls under undefined behavior as defined by the bullets above.
如果我們用 Clang 嘗試這個(gè),我們會(huì)看到一個(gè)不祥的警告(現(xiàn)場(chǎng)觀看):
If we try this with Clang, we see an ominous warning (see it live):
警告:變量 'foo' 在它自己的初始化 [-Wuninitialized] 中使用時(shí)未初始化
warning: variable 'foo' is uninitialized when used within its own initialization [-Wuninitialized]
這是一個(gè)有效的警告,因?yàn)閺奈闯跏蓟淖詣?dòng)變量產(chǎn)生不確定的值是未定義的行為.但是,在這種情況下,您只是綁定了一個(gè)引用并在構(gòu)造函數(shù)中獲取變量的地址,這不會(huì)產(chǎn)生不確定的值并且是有效的.另一方面,以下來(lái)自 C++11 標(biāo)準(zhǔn)草案的自初始化示例:
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 ;
確實(shí)會(huì)調(diào)用未定義的行為.
does invoke undefined behavior.
活動(dòng)問(wèn)題 453:引用只能綁定到有效"對(duì)象 似乎也相關(guān),但仍然是開(kāi)放的.最初提議的語(yǔ)言與缺陷報(bào)告 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.
這篇關(guān)于將 C++ 對(duì)象傳遞給它自己的構(gòu)造函數(shù)是否合法?的文章就介紹到這了,希望我們推薦的答案對(duì)大家有所幫助,也希望大家多多支持html5模板網(wǎng)!