C++ 平凡的析构函数会导致别名吗

C++ 平凡的析构函数会导致别名吗,c++,memory,c++11,destructor,C++,Memory,C++11,Destructor,C++11§3.8.1声明,对于具有平凡析构函数的对象,我可以通过分配给其存储来结束其寿命。我想知道琐碎的析构函数是否可以延长对象的寿命,并通过“销毁一个我在更早的时候就结束了生命的对象”来引起别名问题 首先,一些我知道是安全和免费的东西 void* mem = malloc(sizeof(int)); int* asInt = (int*)mem; *asInt = 1; // the object '1' is now alive, trivial constructor + assign

C++11§3.8.1声明,对于具有平凡析构函数的对象,我可以通过分配给其存储来结束其寿命。我想知道琐碎的析构函数是否可以延长对象的寿命,并通过“销毁一个我在更早的时候就结束了生命的对象”来引起别名问题

首先,一些我知道是安全和免费的东西

void* mem = malloc(sizeof(int));
int*  asInt = (int*)mem;
*asInt = 1; // the object '1' is now alive, trivial constructor + assignment
short*  asShort = (short*)mem;
*asShort = 2; // the object '1' ends its life, because I reassigned to its storage
              // the object '2' is now alive, trivial constructor + assignment
free(mem);    // the object '2' ends its life because its storage was released
现在,对于一些不太清楚的事情:

{
    int asInt = 3; // the object '3' is now alive, trivial constructor + assignment
    short* asShort = (short*)&asInt; // just creating a pointer
    *asShort = 4; // the object '3' ends its life, because I reassigned to its storage
                  // the object '4' is now alive, trivial constructor + assignment
    // implicitly, asInt->~int() gets called here, as a trivial destructor
}   // 'the object '4' ends its life, because its storage was released
§6.7.2规定自动存储持续时间的对象在作用域结束时被销毁,表明析构函数被调用如果有一个int要销毁,
*asShort=2
是一个别名冲突,因为我正在取消引用一个不相关类型的指针。但是,如果整数的寿命在
*asShort=2
之前结束,那么我正在短时间内调用int析构函数。

关于这一点,我看到了几个相互竞争的部分:

§3.8.8内容如下:

如果程序以静态(3.7.1)、线程(3.7.2)或自动(3.7.3)结束T类型对象的生存期 如果T有一个非平凡的析构函数,39程序必须确保 当隐式析构函数调用发生时,原始类型占用相同的存储位置;否则 程序的行为未定义

在我看来,他们用非平凡析构函数调用类型T以产生未定义的行为,这一事实似乎表明在该存储位置中定义了一个不同的类型和平凡析构函数,但我在规范中找不到定义该类型的任何地方

如果一个平凡的析构函数被定义为一个noop,那么这样的定义就很容易了,但是在规范中很少有关于它们的内容

§6.7.3表示允许goto跳入或跳出其变量具有平凡构造函数和平凡析构函数的作用域。这似乎暗示了一种允许跳过琐碎的析构函数的模式,但是规范中关于销毁作用域末尾的对象的前面一节没有提到这一点

最后,还有一句时髦的话:

§3.8.1表示允许我在任何时候开始一个对象的生命周期,如果它的构造函数微不足道的话。这似乎表明我可以做类似的事情

{
    int asInt = 3;
    short* asShort = (short*)&asInt;
    *asShort = 4; // the object '4' is now alive, trivial constructor + assignment
    // I declare that an object in the storage of &asInt of type int is
    // created with an undefined value.  Doing so reuses the space of
    // the object '4', ending its life.

    // implicitly, asInt->~int() gets called here, as a trivial destructor
}

这些阅读中唯一一个似乎表明存在任何别名问题的是§6.7.2本身。看起来,当作为整个规范的一部分阅读时,微不足道的析构函数不应该以任何方式影响程序(尽管出于各种原因)。有人知道在这种情况下会发生什么吗?

在第二段代码中:

{
    int asInt = 3; // the object '3' is now alive, trivial constructor + assignment
    short* asShort = (short*)&asInt; // just creating a pointer
    *asShort = 4; 
    // Violation of strict aliasing. Undefined behavior. End of.
}
这同样适用于您的第一个代码段。它不是“安全的”,但它通常会工作,因为(a)没有特定的理由来实现编译器,使其无法工作,(b)在实践中,编译器必须至少支持一些违反严格别名的行为,否则就不可能使用编译器实现内存分配器

据我所知,如果您事后阅读
asInt
,DFA可以“检测”到
asInt
未被修改(因为它仅通过严格的别名冲突进行修改,即UB),并在写入
*asShort
后移动
asInt
的初始化。这是我们对标准的任何一种解释——在我的解释中是因为严格的别名冲突,在你的解释中是因为
asInt
在其生命周期结束后读取。所以我们都很高兴这不起作用

但是我不同意你的解释。如果您认为分配给<代码> > AsIt< <代码>的一部分的存储结束了 AtIt < /C> >的生存期,那就是自动对象的生命周期是其范围的声明的直接矛盾。好的,我们可以接受这是一般规则的例外。但这意味着以下内容无效:

{
    int asInt = 0;
    unsigned char *asChar = (unsigned char*)&asInt;
    *asChar = 0; // I've assigned the storage, so I've ended the lifetime, right?
    std::cout << asInt; // using an object after end of lifetime, undefined behavior!
}
{
int-asInt=0;
无符号字符*asChar=(无符号字符*)&asInt;
*asChar=0;//我已经分配了存储,所以我结束了生命周期,对吗?

std::cout我不能说我有所有的答案,因为这是我努力消化的标准的一部分,它不是琐碎的(委婉地说是非常复杂的)。尽管如此,由于我不同意Steve Jessop的答案,我的看法如下

void f() {
   alignas(alignof(int)) char buffer[sizeof(int)];
   int *ip = new (buffer) int(1);                 // 1
   std::cout << *ip << '\n';                      // 2
   short *sp = new (buffer) short(2);             // 3
   std::cout << *sp << '\n';                      // 4
}
void f(){
alignas(alignof(int))字符缓冲区[sizeof(int)];
int*ip=new(buffer)int(1);//1

std::难道这个问题是作为有关别名的相关讨论的一部分出现的吗:很抱歉,这个问题与别名有什么关系?@KerrekSB:显然
asInt
asShort
别名。根据编译器对平凡析构函数的处理方式,我可能会同时引用同一个位置as是一个短字符,作为一个整数,而不是一个整数,然后是一个短字符。@BenVoigt:我看不到连接。别名是“访问存储值”通过类型不匹配的glvalue来定义对象。这里不会发生这种情况。一个对象的生命周期结束,另一个对象的生命周期开始。我同意这是标准中令人尴尬的一部分,但我希望看到对标准中感知到的问题或矛盾的更清晰的描述。你能扩展到(b)吗?我想不出一个场景,但它可能只是在我的脑海中闪过。@GManNickG:我没有检查C++11,但C++03实际上并没有说你可以使用类型为
int
的左值访问
char
对象或数组,它只说了相反的话。也许我错了,但我认为这会威胁到基础。对象生命周期规则s是美丽的法律术语,不幸的是,它们相互矛盾。整个“一个初始化很简单的对象在获得存储(适当大小和对齐)后就开始其生命周期”是一个特别有问题的问题,因为它