简体   繁体   English

C ++ 11成员初始值设定项列表与类初始值设定项?

[英]C++11 member initializer list vs in-class initializer?

What difference between these ways of initializing object member variables in C++11 ? 这些在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]: §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. mem初始化程序中expression-listbraced-init-list用于根据直接初始化的8.5初始化规则来初始化指定的子对象(或在委派构造函数的情况下,是完整的类对象) [...] [...]

  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 在非委托构造函数中,如果给定的非静态数据成员或基类未由mem-initializer-id指定(包括没有mem-initializer-list的情况,因为构造函数没有ctor-initializer )并且该实体不是抽象类(10.4)的虚拟基类,则

    — 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 ; —如果实体是具有brace-or-equal-initializer的非静态数据成员,则按照8.5中的指定进行初始化;

§8.5 [dcl.init]: §8.5[dcl.init]:

  1. The initialization that occurs in the form 初始化形式

    T x = a; Tx = 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 . 以及参数传递,函数返回,引发异常(15.1),处理异常(15.3)和聚合成员初始化(8.5.1)的过程都称为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). member-initializer-list上初始化非静态数据成员遵循直接初始化的规则,该规则不会创建需要移动/复制的中间临时对象(如果在没有复制删除的情况下进行编译),也不会创建中间临时对象。数据成员必须是可复制/可移动的(即使删除了副本)。 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). 另外, 直接初始化引入了显式上下文,而复制初始化不是显式的(如果为初始化选择的构造函数是explicit ,则程序将无法编译)。

In other words, the obj s = obj("value"); 换句话说, obj s = obj("value"); syntax won't compile if obj is declared as: 如果obj声明为:语法将无法编译:

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). 在禁用复制删除的情况下 ,使用复制初始化语法时,每个实例都有一个附加的复制/移动构造函数调用( obj s = obj("value");是其中之一)。


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. (仅C ++ 11,直到C ++ 14)使用brace-or-equal-initializer的类违反了聚合类型的约束。
  4. With the brace-or-equal-initializer syntax it's not possible to perform a direct-initialization other than a direct-list-initialization . 使用大括号或相等初始化器语法,除直接列表初始化之外,无法执行直接 初始化

Both examples are equivalent. 这两个例子是等效的。
Though only if the type is copyable or movable (check it for yourself) and NRVO is actually done (any halfway decent compiler will do it as a matter of course). 尽管只有当类型是可复制或可移动的(自己检查)并且实际完成NRVO时,(当然,任何中途的编译器都可以这样做)。

Though if you had many constructors and constructor-chaining were inappropriate, the first method would allow you not to repeat yourself. 尽管如果您有很多构造函数并且构造函数链接不合适,则第一种方法将使您不再重复自己。

Also, you can use that method to define aggregates with defaults different from aggregate-initialization for (some) members since C++14. 另外,您可以使用该方法定义具有默认值的聚合,此默认值不同于自C ++ 14以来(某些)成员的聚合初始化。

They are the same. 他们是一样的。

Neither is better than the other in terms of performance, and there is no other way to initialise them. 在性能方面,没有一个比另一个更好,并且没有其他方法可以初始化它们。

The benefit of in-class initialisation (the first in your example) is that the order of initialisation is implicit. 类内初始化(您的示例中的第一个)的好处是初始化的顺序是隐式的。 In initialiser list you have to explicitly state the order - and compilers will warn of out-of-order initialisation if you get the ordering incorrect. 在初始化程序列表中,您必须明确说明顺序-如果顺序不正确,编译器将警告乱序初始化。

From the standard: 从标准:

12.6.2.5
nonstatic data members shall be initialized in the order they were declared 
in the class definition

If you get the order wrong in your list, GCC will complain: 如果您在列表中输入的订单有误,GCC将投诉:

main.cpp: In constructor 'C::C()':
main.cpp:51:9: warning: 'C::b' will be initialized after
main.cpp:51:6: warning:   'int C::a'

The benefit of initialiser lists is perhaps a matter of taste - the list is explicit, typically in the source file. 初始化程序列表的好处可能在于您的口味-列表是显式的,通常在源文件中。 In-class is implicit (arguably), and is typically in the header file. 类内是隐式的(可以争论),通常在头文件中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM