【发布时间】:2022-01-12 22:54:42
【问题描述】:
在运行这个简单的程序时,会观察到不同的行为,具体取决于编译器。
当使用 GCC 11.2 编译时,它会打印 true,当使用 MSVC 19.29.30137 编译时会打印 false(两者都是今天的最新版本)。
#include <type_traits>
#include <iostream>
struct S {
int a;
S() = delete;
S(S const &) = delete;
S(S &&) = delete;
S &operator=(S const &) = delete;
S &operator=(S &&) = delete;
~S() = delete;
};
int main() {
std::cout << std::boolalpha;
std::cout << std::is_trivially_copyable_v<S>;
}
相关引用(来自最新的 C++23 工作草案N4901):
给定 20.15.5.4 [meta.unary.prop],如果 T 是 6.8.1/9 [basic.types.general] 中定义的 trivially copyable type,则 std::is_trivially_copyable_v<T> 被定义为真:
算术类型(6.8.2)、枚举类型、指针类型、指向成员类型(6.8.3)、std::nullptr_t、 这些类型的 cv 限定(6.8.4)版本统称为标量类型。标量类型,微不足道 可复制的类类型 (11.2)、此类类型的数组以及这些类型的 cv 限定版本统称为 可简单复制的类型。
trivially copyable class types 在 11.2/1 [class.prop] 中定义:
1 可平凡复制的类是一个类:
——至少有一个符合条件的复制构造函数、移动构造函数、复制赋值运算符或移动赋值运算符(11.4.4、11.4.5.3、11.4.6),
——其中每个符合条件的复制构造函数、移动构造函数、复制赋值运算符和移动赋值运算符都是微不足道的,并且
——它有一个普通的、未删除的析构函数 (11.4.7)。
合格(11.4.4 [特殊]):
1 默认构造函数 (11.4.5.2)、复制构造函数、移动构造函数 (11.4.5.3)、复制赋值运算符、 移动赋值运算符 (11.4.6) 和预期析构函数 (11.4.7) 是特殊的成员函数。
6 一个合格的特殊成员函数是一个特殊的成员函数:
——函数没有被删除,
——如果有的话,相关的约束(13.5)得到满足,并且
——没有同类的特殊成员函数更受约束
trivial 用于这些函数(定义在 11.4.5.3/11 [class.copy.ctor]、11.4.6/9 [class.copy.assign]、11.4.7/8 [class.dtor])一般是指:
- 该功能不是用户提供的。
- 类没有虚拟的东西
- 每个非静态数据成员都有相关的琐碎功能
根据 9.5.2/5 [dcl.fct.def.default],在提供的程序中删除的功能不是用户提供的:
...如果函数是用户声明的,则该函数是用户提供的 并且在第一次声明时没有明确默认或删除。 ...
如果我的理解是正确的,struct S 有deleted special member functions 使它们非eligible,这不符合trivially copyable class type 和trivially copyable type 的要求。因此,符合行为是 MSVC 的。这是正确的吗?
【问题讨论】:
-
好吧,在你在这里发帖的同时,你可以把你的发现发给 g++ 团队,让他们看看。
-
Compiler comparison 供参考。 GCC 和 Clang 都将
S视为 C++17 及更高版本的可简单复制,但不适用于 C++14 及更低版本。 -
@JeffGarrett 为什么不从缺陷中提取相关信息并作为答案发布?
-
@SergeyA,时间。 :) 我现在已经发布了。
标签: c++ language-lawyer c++20 typetraits