当前位置: 首页 > 知识库问答 >
问题:

为什么GCC和MSVCis_trivially_copyable_v不同?

危钱明
2023-03-14

运行这个简单的程序时,根据编译器的不同,观察到不同的行为。

它在由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 [元一元道具],标准::is_trivially_copyable_v

这些类型的算术类型(6.8.2)、枚举类型、指针类型、指向成员的指针类型(6.8.3)、std::nullptr_t和cv限定的(6.8.4)版本统称为标量类型。标量类型、普通可复制类类型(11.2)、此类类型的数组以及这些类型的cv限定版本统称为普通可复制类型。

其中可简单复制的类类型定义在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)(如果有),以及

— 没有相同类型的特殊成员函数受到更多约束

这些函数的< code >平凡(如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删除了特殊成员函数,使它们不符合 普通可复制类类型 一般可复制类型的要求。因此,符合性行为是MSVC的。这是正确的吗?

共有1个答案

夔宏深
2023-03-14

GCC和Clang报告说,< code>S在C 11到C 23标准模式下很容易被复制。MSVC报告说,< code>S在C 14到C 20标准模式下是不可复制的。

N3337 (~ C 11)和N4140 (~ C 14)说:

普通可复制类是指:

  • 没有重要的复制构造函数,
  • 没有重要的移动构造函数,
  • 没有重要的复制赋值操作符,
  • 没有重要的移动赋值操作符,并且
  • 有一个微不足道的析构函数。

根据这个定义S是可以简单复制的。

N4659 (~ C 17) 说:

一个普通的可复制类是这样一个类:

  • 其中每个复制构造函数、移动构造函数、复制赋值操作符和移动赋值操作符要么被删除,要么被忽略,
  • 至少有一个未删除的复制构造函数、移动构造函数、复制赋值操作符或移动赋值操作符,以及
  • 有一个微不足道的、未删除的析构函数

根据这个定义,S是不可复制的。

N4860 (~ C 20) 说:

一个普通的可复制类是这样一个类:

    < li >至少有一个合格的复制构造函数、移动构造函数、复制赋值运算符或移动赋值运算符, < li >其中每个合格的复制构造函数、移动构造函数、复制赋值运算符和移动赋值运算符都是平凡的,并且 < li >它有一个普通的、未删除的析构函数。

根据这个定义,S是不可复制的。

因此,正如所发布的,S在C11和C14中是可以复制的,但在C17和C20中则不是。

此更改是从 2016 年 2 月的 DR 1734 中采用的。实施者通常将 DR 视为按惯例适用于所有先前的语言标准。因此,根据已发布的 C 11 和 C 14 标准,S 是可复制的,并且按照惯例,较新的编译器版本可能会选择在 C 11 和 C 14 模式下将 S 视为不可复制。因此,对于 C 11 和 C 14,所有编译器都可以说是正确的。

对于C 17及更高版本,S显然不是可以轻松复制的,因此GCC和Clang是不正确的。这是GCC错误#96288和LLVM错误#39050

 类似资料:
  • 发展至今(2020 年 6 月份),GCC 编译器已经更新至 10.1.0 版本,其功能也由最初仅能编译 C 语言,扩增至可以编译多种编程语言,其中就包括 C++ 。 除此之外,当下的 GCC 编译器还支持编译 Go、Objective-C,Objective-C ++,Fortran,Ada,D 和 BRIG(HSAIL)等程序,甚至于 GCC 6 以及之前的版本还支持编译 Java 程序。但本

  • 对于 GCC 的认知,很多读者还仅停留在“GCC 是一个C语言编译器”的层面,是很片面的。从本节开始,我将带领大家系统学习 GCC,本节先带领大家系统地了解一下 GCC。 谈到 GCC,就不得不提 GNU 计划。GNU 全称 GNU's Not UNIX,又被称为“革奴计划”,由理查德·斯托曼于 1983 年发起。GNU 计划的最终目标是打造出一套完全自由(即自由使用、自由更改、自由发布)、开源的

  • 这是一个使用ValArray的简单c程序: 如果我像这样编译并运行它: 产出如预期: 但是,如果我像这样编译和运行它: 输出为: 如果使用优化参数,也会发生同样的情况。 GCC版本是(Archlinux最新版本): 但是,如果我尝试叮当,两者 和 产生相同的正确结果: clang版本是: 我还尝试了在Debian上使用GCC 4.9.2,其中可执行文件会产生正确的结果。 这是GCC中可能存在的错误

  • 为什么在那里插入? 如果没有一个好的理由:我能用什么方法摆脱它吗? 如果您想使用以下示例:https://godbolt.org/z/74ycy63se

  • 我正在编写一些AVX代码,需要从可能未对齐的内存中加载。我目前正在加载4个double,因此我将使用内部指令\u mm256\u loadu\u pd;我编写的代码是: 然后,我使用选项O3-mavx-g进行编译,然后使用objdump获得汇编程序代码以及带注释的代码和行(

  • 我会直接去MCVE: GCC 7.2和7.1拒绝编译,错误如下: 您可以在没有任何标志的情况下复制,也可以使用