在C++中构造和初始化普通类型

Dan*_*l H 9 c++ initialization language-lawyer

一个普通的类很容易复制,并且有一个简单的默认构造函数(一个普通的类型是其中之一或类似工作的内置类).

由于可以使用memcpy复制琐碎类型的对象,并且由于缺省初始化琐碎类型不会改变任何在representation¹字节的,将执行以下操作的代码(使用C++ 20概念)正确地初始化passed-的副本在对象?

#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;
}
Run Code Online (Sandbox Code Playgroud)

(编译器资源管理器,提供了几个实例)

看起来这个代码可以工作,因为会有一个具有正确对象表示的初始化对象.但是,鉴于初始化没有任何反应,对象是否会有一个不确定的值 ²?


¹未在单个位置指定,但默认初始化的链接过程调用构造函数,必须隐式定义构造函数 ; 隐式定义的构造函数default-initialize所有成员和基础 ; 并且对于内置的普通类型,在没有初始化的情况下递归到底.

² [expr.new] p18.1中的注释表示它会,但注释是非规范性的,只有非放置的情况才是合理的new.

Nic*_*las 7

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

不,不是的.

它确实返回一个指向有效的指针T,但标准中没有任何内容要求它的值是值的T副本orig.

据称普通类型的默认初始化执行"无初始化".但这与保留该内存的当前存储空间不同:[dcl.init]/12

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

请注意,它说,它保留了" 一个不确定的值",而不是"这是在内存中相同的价值".如果没有明确的保护,标准不要求实现保留内存的内容.

考虑调试构建.为了捕获错误,"不执行初始化"的情况有时会使用特定字节填充未初始化的内存,以便您可以检测何时访问未初始化的内存.如果"不确定的值"不能保留内存中的当前值,那么这对于实现来说是合法的.

在C++中,没有办法用另一个对象的逐字节副本初始化一个对象.你可以做的memcpy的到任意的内存,但你不能表现在记忆这需要其值的对象内存.并且您可以memcpy到现有对象,但该对象已经被初始化(除非在创建时没有执行初始化).

因此,您可以做的最好的事情是颠倒两个语句的顺序.