翻译:怎样理解C++ 11中的trivial和standard-layout---An answer from stackoverflow

上一篇译文中,我们了解到C++中的Aggregates和POD类型,那么在C++ 11中这些定义会有怎样的改变呢,什么是trivial和standard-layout类型呢?相信在这篇译文中,可以找到你想要的答案

-------------------------------------------------------------------------译文

C++ 11 做了哪些改变?

Aggregates

C++标准中Aggregate的定义稍微有所改变,但是之前的定义基本相同:

一个Aggregate是一个数组或者一个没有用户声明构造函数,没有{ }和=直接初始化(brace-or-equal-initializers)的非静态类型成员,没有私有或保护类型的非静态数据成员,没有父类和虚函数的类型

OK,做了哪些改变?

  1. 之前,aggregate类型不能拥有用户声明的构造函数,现在,不能拥有用户提供的构造函数,有什么区别呢?因为现在你可以声明构造函数并设置为default。这依然是Aggregate类型,因为在声明时任何使用默认实现的构造函数(或者其他特殊成员函数)都不是用户提供的。
    1 struct Aggregate {
    
    2     Aggregate() = default; // asks the compiler to generate the default implementation
    
    3 };

     

  2. 现在,Aggregate类型不能拥有任何{ }和=直接初始化(brace-or-equal-initializers)的非静态类型成员,什么意思呢?就是因为在新的标准下,我们可以像这样直接声明成员变量:
    1 struct NotAggregate {
    
    2     int x = 5; // valid in C++11
    
    3     std::vector<int> s{1,2,3}; // also valid
    
    4 };

     

 

Aggregate类型不允许使用这种特性,因为这相当于提供了用户自定义的默认构造函数,所以,什么是Aggregate类型,在新标准中并没有做太多改变,基本概念还是一样的,只不过适用于C++的新特性而已。

PODs呢?

PODs类型有了很多改动。很多之前POD类型的限制在新标准中被放开了,而且,定义的方式也被彻底的改变了。 

POD的含义可以从两个显著的特性说明:

  1. 它支持静态初始化,而且
  2. 在C++中编译POD类型会和C中编译的struct类型得到相同的内存布局

正是因为这个,这个定义被划分为两个不同的概念:trivial 类型和standard-layout 类型,因为这比POD类型更有用。新标准中已经很少使用POD这个术语了,而是更多的在使用更精确的概念:trival和stand-layout。

新的定义基本说明了POD类型既是trivial类型有事stand-layout,而且,这个特性递归地适用于每个非静态数据成员:

POD struct类型是既为trivial类型又为standard-layout类型,而且还没有非静态类型的non-POD struct和non-POD union(或者这些类型的数组)数据成员的non-union类型。相似地,POD union类型是极为trivial类型尤为standard-layout类型,而且还没有非静态类型的non-POD struct和non-POD union(或者这些类型的数组)数据成员的union类型。POD 类型包含POD struct和POD union这两种类型。

我们来分开、详细说明这两个特性。

Trivial 类型

上面提到的第一个特性Trivial:trivial类型支持静态初始化。如果一个类型是拷贝不变的(trivially copyable),使用memcpy这种方式把它的数据从一个地方拷贝出来会得到相同的结果。 

C++标准把trivial类型定义如下:

一个拷贝不变(trivially copyable)类型是指:

  • 没有non-trivial 的复制构造函数

  • 没有non-trivial的转移构造函数

  • 没有non-trivial的赋值操作符
  • 没有non-trivial的转移赋值操作符

  • 有一个trivial的析构函数

一个trivial class类型是指有一个trivial类型的默认构造函数,而且是拷贝不变的(trivially copyable)的class。(特别注意,拷贝不变类型和trivial类型都不能有虚函数和虚基类)。

那么,这么trivial和non-trivial类型到底是什么呢?

Class X复制或转移构造函数是trivial类型的,如果他不是用户提供的,而且

  • Class X没有任何虚函数和虚基类,而且
  • 用于复制或转移直接基类的构造函数是trivial类型的,而且
  • 复制或转移构造函数选择复制或转移的X内部的每一个非静态数据成员(或数组)必须是trivial类型的

否则,复制或转移构造函数就是non-trivial类型的

从根本上也就是说复制或转移构造函数是trivial类型的只要他不是用户提供的、类内部没有虚函数,而且这个规则要递归地适用于所有数据成员类型和基类。

Trivial类型赋值或转移操作符的定义类似,把构造函数替换为赋值操作符就可以了。

Trivial类型的析构函数也有类似的定义,不过要加一条限制,就是不能为虚函数。

Trivial类型的默认构造函数也需要加一条限制:上面我们已经看到了,不能拥有{ }或=初始化的(brace-or-equal-initializers)非静态数据成员。

 这里有个几个例子能让你彻底明白每个类型:

 1 // empty classes are trivial

 2 struct Trivial1 {};

 3 

 4 // all special members are implicit

 5 struct Trivial2 {

 6     int x;

 7 };

 8 

 9 struct Trivial3 : Trivial2 { // base class is trivial

10     Trivial3() = default; // not a user-provided ctor

11     int y;

12 };

13 

14 struct Trivial4 {

15 public:

16     int a;

17 private: // no restrictions on access modifiers

18     int b;

19 };

20 

21 struct Trivial5 {

22     Trivial1 a;

23     Trivial2 b;

24     Trivial3 c;

25     Trivial4 d;

26 };

27 

28 struct Trivial6 {

29     Trivial2 a[23];

30 };

31 

32 struct Trivial7 {

33     Trivial6 c;

34     void f(); // it's okay to have non-virtual functions

35 };

36 

37 struct Trivial8 {

38      int x;

39      static NonTrivial1 y; // no restrictions on static members

40 }

41 

42 struct Trivial9 {

43      Trivial9() = default; // not user-provided

44       // a regular constructor is okay because we still have default ctor

45      Trivial9(int x) : x(x) {};

46      int x;

47 }

48 

49 struct NonTrivial1 : Trivial 3 {

50     virtual f(); // virtual members make non-trivial ctors

51 }

52 

53 struct NonTrivial2 {

54     NonTrivial2() : z(42) {} // user-provided ctor

55     int z;

56 }

57 

58 struct NonTrivial3 {

59     NonTrivial3(); // user-provided ctor

60     int w;

61 }

62 NonTrivial3::NonTrivial3() = default; // defaulted but not on first declaration

63                                       // still counts as user-provided

64 struct NonTrivial5 {

65     virtual ~NonTrivial5(); // virtual destructors are not trivial

66 };

Standard-layout

Standard-layout是第二个特性,C++标准中说它对语言间交互很有用,这是因为C++ standard-layout类型和C中struct或union类型有相同的内存布局。 

这是另一个需要所有成员和基类递归遵循的特性。而且,虚函数和虚基类也是不允许的,这会让内存布局与C不兼容。 

这里有一个规定放开了,那就是standard-layout类型的非静态数据成员必须是相同的访问控制,之前他们都必须是public类型的,但是现在他们可以是private或protected类型的了,只要他们都属于同一种。 

当使用继承时,在整个继承体系中,只允许一个类拥有非静态数据成员,而且第一个非静态数据成员不能是基类的(这可能打乱量化规则),否则,就不是standard-layout类型。 

C++标准中的是这样定义的:

standard-layout 类型的类是指:

  • 没有 non-standard-layout类型(或这些类型的数组)和引用的非静态数据成员
  • 没有虚函数和虚基类

  • 非静态数据成员的访问控制必须是相同的
  • 没有non-standard-layout的基类
  • 在最底层的派生类中没有非静态数据成员,而且在最多有一个基类拥有非静态数据成员,或者没有拥有非静态数据成员

  • 相同基类类型的非静态数据成员不能作为第一个成员

standard-layout类型struct就是以struct或class为关键字定义的standard-layout 类型。

standard-layout类型union就是以union为关键字定义的standard-layout 类型。

[注意:standard-layout类型在C++与其他语言交互时非常重要]

现在我们来看几个例子:

 

 1 // empty classes have standard-layout

 2 struct StandardLayout1 {};

 3 

 4 struct StandardLayout2 {

 5     int x;

 6 };

 7 

 8 struct StandardLayout3 {

 9 private: // both are private, so it's ok

10     int x;

11     int y;

12 };

13 

14 struct StandardLayout4 : StandardLayout1 {

15     int x;

16     int y;

17 

18     void f(); // perfectly fine to have non-virtual functions

19 };

20 

21 struct StandardLayout5 : StandardLayout1 {

22     int x;

23     StandardLayout1 y; // can have members of base type if they're not the first

24 };

25 

26 struct StandardLayout6 : StandardLayout1, StandardLayout5 {

27     // can use multiple inheritance as long only

28     // one class in the hierarchy has non-static data members

29 };

30 

31 struct StandardLayout7 {

32     int x;

33     int y;

34     StandardLayout7(int x, int y) : x(x), y(y) {} // user-provided ctors are ok

35 };

36 

37 struct StandardLayout8 {

38 public:

39     StandardLayout8(int x) : x(x) {} // user-provided ctors are ok

40 // ok to have non-static data members and other members with different access

41 private:

42     int x;

43 };

44 

45 struct StandardLayout9 {

46     int x;

47     static NonStandardLayout1 y; // no restrictions on static members

48 };

49 

50 struct NonStandardLayout1 {

51     virtual f(); // cannot have virtual functions

52 };

53 

54 struct NonStandardLayout2 {

55     NonStandardLayout1 X; // has non-standard-layout member

56 };

57 

58 struct NonStandardLayout3 : StandardLayout1 {

59     StandardLayout1 x; // first member cannot be of the same type as base

60 };

61 

62 struct NonStandardLayout4 : StandardLayout3 {

63     int z; // more than one class has non-static data members

64 };

65 

66 struct NonStandardLayout5 : NonStandardLayout3 {}; // has a non-standard-layout base class

结论:

在新的标准下,很多新类型成为POD类型,而且,就算一个类型不是POD类型,我们也可以分别利用POD类型的特性(只要这个类型是trivial或者standard-layout)。

标准模板块(STL)在头文件<type_traits>中定义了对这些类型的检测:

1 template <typename T>

2 struct std::is_pod;

3 template <typename T>

4 struct std::is_trivial;

5 template <typename T>

6 struct std::is_trivially_copyable;

7 template <typename T>

8 struct std::is_standard_layout;

 

你可能感兴趣的:(overflow)