C ++中琐碎类型的构造和初始化

时间:2018-07-15 02:44:02

标签: c++ initialization language-lawyer

trivial class可以被复制并且具有一个trivial default constructortrivial type可以是其中之一,也可以是工作类似的内置类)。

由于you can use memcpy复制了普通类型的对象,并且由于default-initializing普通类型不会更改表示形式中的任何字节,¹,以下代码(使用C ++ 20概念)可以正确执行初始化传入对象的副本?

#include <cstdlib>
#include <cstring>

#include <new>
#include <type_traits>

template <typename T>
T* copy_trivial(T orig) requires std::is_trivial_v<T>
{
    void* buf = std::aligned_alloc(alignof(T), sizeof(T));

    // Note the order of these statements
    std::memcpy(buf, &orig, sizeof(T));
    return new(buf) T;
}

Compiler Explorer,提供了几个实例)

该代码似乎可以工作,因为将存在带有正确对象表示形式的初始化对象。但是,假设在初始化后 没有任何反应,则该对象会改为具有indeterminate value²吗?


¹没有在单个位置中指定,但是默认初始化的链接过程调用构造函数,该构造函数必须为implicitly defined;隐式定义的构造函数default-initializes all the members and bases;对于内置的普通类型,该递归在no initialization is performed处达到最低点。

²[expr.new]p18.1中的注释表示可以,但是注释不是规范性的,只有非放置new才有可能。

1 个答案:

答案 0 :(得分:7)

  

以下代码(使用C ++ 20概念)是否正确初始化了传入对象的副本?

不,不是。

它确实返回了指向有效T的指针,但是在标准中没有任何内容要求该T的值是orig的副本。 / p>

琐碎类型的默认初始化被称为执行“不初始化”。但这与保留该内存的当前存储不同:[dcl.init] / 12

  

当获得具有自动或动态存储持续时间的对象的存储时,该对象具有不确定的值,并且如果未对该对象执行初始化,则该对象将保留不确定的值,直到替换该值为止。

请注意,它表示保留“ 一个 不确定的值”,而不是“与该内存中相同的值”。在没有显式保护的情况下,该标准不需要实现即可保留内存内容。

考虑调试版本。为了捕获错误,“不执行初始化”的情况有时会用特定的字节填充未初始化的内存,以便您可以检测何时访问未初始化的内存。仅当“不确定值”未将当前值保留在内存中时,这才对实现合法。

在C ++中,无法用另一个对象的按字节拷贝来初始化对象。您可以将memcpy放入任意内存中,但是无法在该内存中显示一个对象,该对象从该内存中获取 的值。您可以将其存入一个现有对象,但是该对象将已经初始化(除非在创建对象时未执行任何初始化)。

因此,您所能做的最好是颠倒这两个语句的顺序。