std :: is_trivially_copyable - 为什么volatile标量类型不能轻易复制?

TRe*_*803 20 c++ standards volatile type-traits c++11

C++ 17的当前标准(我已经观察到类似于C++ 11的措辞)对于简单的可复制类型具有非常混乱的措辞.我首先使用以下代码(GCC 5.3.0)偶然发现了这个问题:

class TrivialClass {};
std::is_trivially_copyable<int volatile>::value; // 0
std::is_trivially_copyable<TrivialClass volatile>::value; // 1 ??
Run Code Online (Sandbox Code Playgroud)

让混乱更加糟糕,我试着检查std::is_trivial一下这件事情有什么说法,只是让人更加困惑.

class TrivialClass {};
std::is_trivial<int volatile>::value; // 1 ??
std::is_trivial<TrivialClass volatile>::value; // 1
Run Code Online (Sandbox Code Playgroud)

很困惑,我检查了最新的C++ 17草案,看看是不是有问题,我发现了一些有些含糊不清的措辞可能是罪魁祸首:

http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4567.pdf#page.73

cv-非限定标量类型,平凡可复制类类型(第9节),此类类型的数组以及这些类型的非易失性const限定版本(3.9.3)统称为平凡可复制类型.

以下是关于平凡可复制类的信息:

http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4567.pdf#page.226

一个简单的可复制类是一个类:

- (6.1)没有非平凡的复制构造函数(12.8),

- (6.2)没有非平凡的移动构造函数(12.8),

- (6.3)没有非平凡的副本分配操作员(13.5.3,12.8),

- (6.4)没有非平凡的移动指派算子(13.5.3,12.8),和

- (6.5)有一个简单的析构函数(12.4).

http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4567.pdf#section.12.8

构造函数:

如果不是用户提供的,则类X的复制/移动构造函数是微不足道的,其参数类型列表等效于隐式声明的参数类型列表,如果

- (12.1)类X没有虚函数(10.3),没有虚基类(10.1),和

- (12.2)类X 没有volatile限定类型的非静态数据成员,和

- (12.3)选择复制/移动每个直接基类子对象的构造函数是微不足道的,并且

- (12.4)对于类型(或其数组)的X的每个非静态数据成员,选择复制/移动该成员的构造函数是微不足道的;

否则复制/移动构造函数是非平凡的.

分配:

如果类X不是用户提供的,则类X的复制/移动赋值运算符是微不足道的,其参数类型列表等效于隐式声明的参数类型列表,如果

- (25.1)类X没有虚函数(10.3),没有虚基类(10.1),和

- (25.2)类X 没有volatile限定类型的非静态数据成员,和

- (25.3)选择复制/移动每个直接基类子对象的赋值运算符是微不足道的,并且

- (25.4)对于类型(或其数组)的X的每个非静态数据成员,选择复制/移动该成员的赋值运算符是微不足道的;

否则复制/移动赋值运算符是非常重要的.

注意:更新了此部分以及更多信息.我现在相信这是GCC中的一个错误.然而,仅凭这一点并不能回答我的所有问题.

我可以看到,也许是因为TrivialClass没有非静态成员,因为它会传递上述规则,所以我添加了一个int,它仍然可以简单地复制.

class TrivialClass { int foo; };
std::is_trivially_copyable<int volatile>::value; // 0
std::is_trivially_copyable<TrivialClass volatile>::value; // 1 ??
Run Code Online (Sandbox Code Playgroud)

该标准规定volatile应该由volatile对象的子对象继承.现在,含义TrivialClass volatile的非静态数据成员foo应该是类型int volatile.

http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4567.pdf#page.76

volatile对象是volatile T类型的对象,这种对象的子对象,或const volatile对象的可变子对象

我们可以通过以下方式确认这在GCC中有效:

std::is_same<decltype(((TrivialClass volatile*)nullptr)->foo), int volatile>::value; // 1! (Expected)
Run Code Online (Sandbox Code Playgroud)

困惑,然后我添加了一个易失性int foo自己.它仍然通过,这显然是一个错误!

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68905#c1

class TrivialClass { int volatile foo; };
std::is_trivially_copyable<int volatile>::value; // 0
std::is_trivially_copyable<TrivialClass volatile>::value; // 1 ??
Run Code Online (Sandbox Code Playgroud)

继续,我们看到它std::is_trivial也按预期工作:

http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4567.pdf#page.73

标量类型,普通类类型(第9节),这些类型的数组和这些类型的cv限定版本(3.9.3)统称为普通类型.

好的,我在这里有很多问题.

  • 为什么挥发性物质是is_trivially_copyable而不是is_trivial?
  • 什么是is_trivially_copyable和对象类型的处理,它是标准的错误还是问题?
  • 为什么事情是不稳定的呢?

任何人都可以帮助我解决这个问题,我真的很茫然.

Tom*_*ski 6

显然,这是修复标准中的缺陷的方式,但您并不是唯一对此感到困惑的人。

\n

来自https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#2094

\n
\n
    \n
  1. 具有易失性成员的类的简单复制/移动构造函数
  2. \n
\n

部分:12.8 [class.copy] 状态:开放 提交者:Daveed Vandevoorde 日期:2015-03-06

\n

问题 496 的解决方案包括添加 12.8 [class.copy] 第 25.2 段,如果类具有 volatile 限定类型的非静态数据成员,则该类的复制/移动构造函数将变得非常重要。此更改违反了 IA-64 ABI,因此已要求 CWG 重新考虑该决议的这方面内容。

\n

相关说明,问题 496 的解决方案还更改了 3.9\n[basic.types] 第 9 段,这使得 volatile 限定的标量类型\n\xe2\x80\x9ctrivial\xe2\x80\x9d 但不是 \xe2\x80 \x9ctrivialy 可复制。\xe2\x80\x9d 目前尚不清楚为什么这里有\n区别;标准中 \xe2\x80\x9ctrivial 类型\xe2\x80\x9d 的唯一实际使用似乎是在 qsort 的描述中,它应该\n可能使用 \xe2\x80\x9ctrivially 可复制的。\xe2\x80\x9d (另见第 1746 期。)

\n
\n

根据问题描述(2004年12月30日起):

\n
\n
    \n
  1. 易失性限定类型真的是 POD 吗?:
  2. \n
\n

然而,在 3.9 [basic.types] 第 3 段中,标准明确\n如果\xe2\x80\x9d POD 是字节的集合,则可以通过\nmemcpy 复制\xe2\x80\x9cas:

\n

对于任何 POD 类型 T,如果指向 T 的两个指针指向不同的 T 对象\nobj1 和 obj2,其中 obj1 和 obj2 都不是基类子对象,\n如果使用 std::memcpy 将 obj1 的值复制到 obj2 \n库函数,obj2 随后应保持与 obj1 相同的值。\n这样做的问题是,可能需要以特定方式\n复制易失性限定类型(例如,在多线程平台上\n仅使用原子操作进行复制)为了避免逐字节复制时可能出现的\xe2\x80\x9c内存\n撕裂\xe2\x80\x9d。

\n

我意识到该标准很少涉及易失性限定类型,而且对于多线程平台也没有任何规定,但是尽管如此,这仍然是一个真正的问题,原因如下:

\n

即将推出的 TR1 将定义一系列特征,提供有关类型属性的信息,包括类型是否是 POD 和/或具有简单的构造/复制/分配操作。库可以使用此信息来适当优化其代码,例如,如果 T 是 POD,则可以使用 memcpy 复制类型 T 的数组,而不是逐元素复制。这是 TR1 类型特征章节背后的主要动机之一。然而,尚不清楚在这些情况下应如何处理易失性类型(或具有易失性类型作为成员的 POD)。2005 年 4 月会议的注释:

\n

目前尚不清楚 volatile 限定符是否真的以这种方式保证原子性。此外,进化工作组正在进行的多线程内存模型工作似乎目前可能为易失性数据指定额外的语义,并且在解决此问题之前需要考虑这项工作。

\n
\n