【问题标题】:Which rules determine whether an object is trivially copyable哪些规则决定一个对象是否可以简单复制
【发布时间】:2015-07-17 18:44:14
【问题描述】:

随着 c++11 的引入,trivially copyableness 变得非常重要。最值得注意的是'std::atomic'的使用。基础非常简单。如果满足以下条件,则 foo 类可以轻松复制:

foo* src = new foo();
foo* dest = malloc(sizeof(foo));
memcpy(dest, src, sizeof(foo));

与以下效果相同:

foo* src = new foo();
foo* dest = new foo(src);

因此,复制内存的对象将具有与复制构造函数相同的效果。但是,当然,这是一个问题。不仅有复制构造函数。但也要移动构造函数,移动赋值运算符。等等。

std::is_trivially_copyable 可以用来测试一个对象是否是普通可复制的。因此,通过反复试验,可以使对象易于复制。

当然,一套定义明确的规则会更好一些:)。所以特此提出我的要求。

【问题讨论】:

  • 您的两个 sn-ps 具有不同的效果,因为一个构造对象而另一个不构造对象。标准库的容器仅在类型为平凡(平凡可复制 + 平凡可构造)时才使用 memcpy 进行复制构造。当类型只能简单地复制时,复制构造函数用于未初始化的范围,而 memcpy 用于已构造的范围。某些操作确实使用了两者,例如 std::vector 的赋值。

标签: c++ c++11 move


【解决方案1】:

定义最明确的规则集直接来自标准。以下是标准草案 N4296 中的相关条目:

[basic.types]/9

中定义了可简单复制的类型

Cv 非限定标量类型、可简单复制的类类型、此类类型的数组和非易失性 这些类型的 const 限定版本统称为普通可复制类型。

[class]/6

中定义了可简单复制的类

平凡可复制类是这样的类:没有不平凡的复制构造函数,没有不平凡的移动构造函数,没有不平凡的复制赋值运算符,没有不平凡的移动赋值运算符,并且具有平凡的析构函数。

[class.copy]/12

中复制/移动构造函数

如果类 X 的复制/移动构造函数不是用户提供的,那么它的参数类型列表等效于隐式声明的参数类型列表,并且如果类 X 没有虚函数并且没有虚拟基类,并且类 X 没有 volatile 限定类型的非静态数据成员,并且选择复制/移动每个直接基类子对象的构造函数是微不足道的,并且对于 X 的每个非静态数据成员类类型(或其数组),选择的构造函数 复制/移动该成员是微不足道的;否则复制/移动构造函数是不平凡的。

[class.copy]/25

中的复制/移动赋值运算符

如果类 X 的复制/移动赋值运算符不是用户提供的,则它的参数类型列表等效于隐式声明的参数类型列表,并且如果类 X 没有虚函数,则它是微不足道的并且没有虚拟基类,并且类 X 没有 volatile 限定类型的非静态数据成员,并且选择用于复制/移动每个直接基类子对象的赋值运算符是微不足道的,并且对于 X 的每个非静态数据成员,是类类型(或其数组),赋值运算符 选择复制/移动该成员是微不足道的; 否则复制/移动赋值运算符是不平凡的。

[class.dtor]/5

中的析构函数

如果析构函数不是用户提供的,并且如果: 析构函数不是虚拟的,则其类的所有直接基类都有普通析构函数,对于其类的所有非静态数据成员对于类类型(或其数组),每个这样的类都有一个微不足道的析构函数。否则,析构函数是不平凡的。

[dcl.fct.def.default]/5

中用户提供的构造函数

显式默认函数和隐式声明函数统称为默认函数, 并且实现应为它们提供隐式定义(12.1 12.4、12.8),这可能意味着将它们定义为已删除。一个函数是用户提供的,如果它是用户声明的并且没有显式默认或者 在第一次声明中删除。用户提供的显式默认函数(即,在其后显式默认) 第一个声明)在显式默认的地方定义;如果隐式定义了这样的函数 删除后,该程序格式错误。

简短的回答是,有时简短的回答比冗长的回答更有帮助。

【讨论】:

    猜你喜欢
    • 2019-01-14
    • 1970-01-01
    • 2016-11-28
    • 1970-01-01
    • 1970-01-01
    • 2015-01-28
    • 1970-01-01
    • 2020-09-14
    相关资源
    最近更新 更多