C ++ 11成员初始化器列表与类内初始化器?

2022-01-18 00:00:00 initialization c++ c++11

这些在 C++11 中初始化对象成员变量的方法有什么区别?还有其他方法吗?哪种方式更好(性能)?:

类任何{上市:obj s = obj("值");任何(){}};

或者

类任何{上市:对象;任何():s(价值"){}};

谢谢.

解决方案

不,这些不一样.

它们之间的区别与 直接初始化 与 复制初始化 的区别是一样的,这很微妙,但通常非常令人困惑.

§12.6.2 [class.base.init]:

<块引用>

  1. mem-initializer中的expression-list或braced-init-list用于初始化指定的子对象(或者,在委托构造函数的情况下,完整的类对象)根据 8.5 的初始化规则进行直接初始化. [...]

  2. 在非委托构造函数中,如果给定的非静态数据成员或基类不是由mem-initializer-id指定的(包括没有mem-initializer-list 因为构造函数没有ctor-initializer)并且实体不是抽象类(10.4)的虚拟基类,那么

    ――如果实体是具有brace-or-equal-initializer的非静态数据成员,则实体按照8.5中的规定进行初始化;p>

§8.5 [dcl.init]:

<块引用>

  1. 表单中发生的初始化

    T x = a;

以及在参数传递、函数返回、抛出异常 (15.1)、处理异常 (15.3) 和聚合成员初始化 (8.5.1) 中称为复制初始化.

在 member-initializer-list 上初始化一个非静态数据成员遵循 direct-initialization 的规则,它不会创建需要的中间临时对象移动/复制(如果编译时没有copy-elision),数据成员的类型都不能是可复制/可移动的(即使副本被省略).此外,直接初始化引入了显式上下文,而复制初始化是非显式的(如果为初始化选择的构造函数是显式,程序不会编译).

换句话说,如果 obj 声明为:

obj s = obj("value"); 语法将无法编译:

struct obj{obj(std::string) {}obj(const obj&) = 删除;};

或:

struct obj{obj(std::string) {}显式 obj(const obj&) {}};

作为一个更具体的例子,虽然下面不会编译:

结构任意{std::atomic<int>a = std::atomic<int>(1);//格式错误:不可复制/不可移动std::atomic<int>b = 2;//格式错误:选择了显式构造函数};

这个会:

结构任意{std::atomic<int>一个;std::atomic<int>乙{2};任何():一个(1){}};

<小时><块引用>

哪种方式更好(性能)?

启用 copy-elision 后两者具有相同的性能.禁用 copy-elision 后,当使用 copy-initialization 语法(即 obj s = obj("value"); 是其中之一.

<小时><块引用>

还有其他方法吗?

brace-or-equal-initializer 语法也允许执行 direct-list-initialization:

类任何{上市:obj s{价值"};任何() {}};

<小时><块引用>

还有其他区别吗?

其他一些值得一提的区别是:

  1. Brace-or-equal-initializer 必须与类声明一起驻留在头文件中.
  2. 如果两者结合,member-initializer-list 优先于 brace-or-equal-initializer(即 brace-or-equal-初始化器 被忽略).
  3. (仅限 C++11,直到 C++14)使用 brace-or-equal-initializer 的类违反聚合类型的约束.
  4. 使用 brace-or-equal-initializer 语法,除了 direct-list-initialization.

What difference between these ways of initializing object member variables in C++11 ? Is there another way ? which way is better (performance) ?:

class any {
  public:
    obj s = obj("value");
    any(){}
};

Or

class any {
  public:
    obj s;
    any(): s("value"){}
};

Thanks.

解决方案

No, these are not the same.

The difference between them is the same that applies for direct-initialization vs. copy-initialization, which is subtle but often very confusing.

§12.6.2 [class.base.init]:

  1. The expression-list or braced-init-list in a mem-initializer is used to initialize the designated subobject (or, in the case of a delegating constructor, the complete class object) according to the initialization rules of 8.5 for direct-initialization. [...]

  2. In a non-delegating constructor, if a given non-static data member or base class is not designated by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer) and the entity is not a virtual base class of an abstract class (10.4), then

    ― if the entity is a non-static data member that has a brace-or-equal-initializer, the entity is initialized as specified in 8.5;

§8.5 [dcl.init]:

  1. The initialization that occurs in the form

    T x = a;

as well as in argument passing, function return, throwing an exception (15.1), handling an exception (15.3), and aggregate member initialization (8.5.1) is called copy-initialization.

Initializing a non-static data member on a member-initializer-list follows the rules of direct-initialization, which doesn't create intermediate temporaries that need to be moved/copied (if compiled without a copy-elision), neither the type of the data member must be copyable/movable (even if the copy is elided). In addition, a direct-initialization introduces an explicit context, while a copy-initialization is non-explicit (if a constructor selected for the initialization is explicit, the program won't compile).

In other words, the obj s = obj("value"); syntax won't compile if obj is declared as:

struct obj
{
    obj(std::string) {}
    obj(const obj&) = delete;
};

or:

struct obj
{
    obj(std::string) {}
    explicit obj(const obj&) {}
};

As a more tangible example, while the below won't compile:

struct any
{
   std::atomic<int> a = std::atomic<int>(1); // ill-formed: non-copyable/non-movable
   std::atomic<int> b = 2; // ill-formed: explicit constructor selected
};

this one will:

struct any
{
    std::atomic<int> a;
    std::atomic<int> b{ 2 };
    any() : a(1) {}
};


Which way is better (performance) ?

With a copy-elision enabled both have identical performance. With copy-elision disabled, there is an additional copy/move constructor call upon every instantiation when the copy-initialization syntax is used (that obj s = obj("value"); is one of).


Is there another way ?

The brace-or-equal-initializer syntax allows one to perform a direct-list-initialization as well:

class any {
public:
    obj s{ "value" };
    any() {}
};


Are there any other differences?

Some other differences that are worth mentioning are:

  1. Brace-or-equal-initializer must reside in a header file along with a class declaration.
  2. If both are combined, member-initializer-list takes priority over brace-or-equal-initializer (that is, brace-or-equal-initializer is ignored).
  3. (C++11 only, until C++14) A class that uses brace-or-equal-initializer violates constraints for an aggregate type.
  4. With the brace-or-equal-initializer syntax it's not possible to perform a direct-initialization other than a direct-list-initialization.

相关文章