【发布时间】:2016-07-06 00:21:28
【问题描述】:
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 类没有 volatile 限定类型的非静态数据成员,并且
— (12.3) 选择复制/移动每个直接基类子对象的构造函数是微不足道的,并且
— (12.4) 对于 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 ??
标准规定 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)
困惑,然后我添加了一个 volatile 到 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 ??
继续前进,我们看到std::is_trivial 也在按预期工作:
http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4567.pdf#page.73
标量类型、平凡类类型(第 9 条)、此类类型的数组以及这些类型的 cv 限定版本(3.9.3)统称为平凡类型。
好的,我这里有很多问题。
- 为什么 volatile 对 is_trivially_copyable 而不是 is_trivial 很重要?
- is_trivially_copyable 和对象类型有什么关系,是标准的错误还是问题?
- 如果某些东西是易变的,这有什么关系?
谁能帮我解决这个问题,我真的很茫然。
【问题讨论】:
-
请注意,标准似乎并没有说类本身必须是 cv-unqualified...但它也没有明确允许 volatile 类。
-
但它仍然明确指出,如果存在 volatile 非静态数据成员,则对象本身不应该是可简单复制的 - 这个限定符是否不会继承到 volatile 对象的子对象?这就是为什么我想知道传递一个空对象是否有意义,但质疑为什么具有非静态数据成员的 volatile 对象也会传递。
-
对我来说,volatile 类的含义并不是很明显。 Volatile 通常用于标量类型,以指示诸如内存映射 I/O 和其他可能在您的程序下发生变化的奇怪东西。我不知道有任何用例将整个类实例放入易失性内存中。
-
挥发性是一个历史性的疣。甚至不推荐in kernel land。事实上,
volatile的唯一目的是禁止优化,这是永远不好的。改用锁。 -
@Dani:请参阅我链接的文章。基本上,您应该为此使用条件变量和锁。
volatile对优化有害。另请参阅this excellent post on the topic。
标签: c++ c++11 standards volatile typetraits