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)统称为普通类型。
好的,我在这里有很多问题。
任何人都可以帮助我解决这个问题,我真的很茫然。
答案 0 :(得分:4)
显然这是标准中的缺陷被修复的方式,但你不是唯一一个对此感到困惑的人。
来自http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2094:
- 具有易失性成员的类的简单复制/移动构造函数
醇>栏目: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日):
- 挥发性合格的类型真的是POD吗? :
醇>然而,在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数据指定了额外的语义,并且在解决此问题之前需要考虑这项工作。