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

时间:2016-03-19 05:01:28

标签: c++ c++11 standards volatile typetraits

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 ??

让混乱更加严重,我试着检查一下std::is_trivial对这件事情的看法,只会让人更加困惑。

class TrivialClass {};
std::is_trivial<int volatile>::value; // 1 ??
std::is_trivial<TrivialClass volatile>::value; // 1

感到困惑,我检查了最新的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类没有挥发性合格类型的非静态数据成员

     

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

     

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

     

否则复制/移动构造函数不重要。

分配:

  

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

     

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

     

- (25.2)X类没有挥发性合格类型的非静态数据成员

     

- (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 ??

该标准规定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)

困惑,然后我向int foo本身添加了一个volatile。它仍然通过,这显然是一个错误!

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 ??

继续,我们看到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和对象类型有什么关系,是标准的错误还是问题?
  • 为什么事情发生变化无关紧要?

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

1 个答案:

答案 0 :(得分:4)

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

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

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

    栏目:12.8 [class.copy]状态:公开发布者:Daveed Vandevoorde日期:2015-03-06

         

    包括问题496的决议   增加12.8 [class.copy]第25.2段,制作一个班级   如果复制/移动构造函数具有非静态数据成员,则它非常重要   挥发性合格的类型。这种变化打破了IA-64 ABI,所以它   已要求CWG重新考虑该决议的这一方面。

         

    在相关的说明中,问题496的决议也改变了3.9   [basic.types]第9段,它使得符合volatile的标量类型   “琐碎的”,但不是“平凡的可复制的”。目前尚不清楚为什么会有   这里的区别;唯一真正使用“琐碎式”的   标准似乎在qsort的描述中,应该是   可能使用“平凡的可复制的”。(参见问题1746。)

从问题描述(2004年12月30日):

  
      
  1. 挥发性合格的类型真的是POD吗? :
  2.         

    然而,在3.9 [basic.types]第3段中,标准清楚地说明了这一点   POD可以“仿佛”它们是一个字节集合   的memcpy:

         

    对于任何POD类型T,如果指向T的两个指针指向不同的T对象   obj1和obj2,其中obj1和obj2都不是基类子对象,   如果使用std :: memcpy将obj1的值复制到obj2中   库函数,obj2随后应保持与obj1相同的值。   这样做的问题是可能需要使用volatile类型   以特定方式复制(通过仅使用原子操作进行复制)   多线程平台,例如),以避免“记忆   撕裂“可能会发生逐字节复制。

         

    我意识到标准对挥发性合格的说法很少   关于多线程平台的类型,什么都没有(但是)   尽管如此,这是一个真正的问题,原因如下:

         

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

         

    目前尚不清楚volatile限定符是否实际上以这种方式保证了原子性。此外,由Evolution工作组完成的多线程内存模型的工作似乎在这一点上可能为volatile数据指定了额外的语义,并且在解决此问题之前需要考虑这项工作。