为什么使用=来初始化C ++中的原始类型?

时间:2021-12-15 16:30:51

Where I work, people mostly think that objects are best initialised using C++-style construction (with parentheses), whereas primitive types should be initialised with the = operator:

在我工作的地方,人们通常认为对象最好使用C ++样式构造(带括号)初始化,而原始类型应该用=运算符初始化:

std::string strFoo( "Foo" );
int nBar = 5;

Nobody seems to be able to explain why they prefer things this way, though. I can see that std::string = "Foo"; would be inefficient because it would involve an extra copy, but what's wrong with just banishing the = operator altogether and using parentheses everywhere?

但是,似乎没有人能够用这种方式解释他们为什么喜欢这样的东西。我可以看到std :: string =“Foo”;会有效率低,因为它会涉及额外的副本,但是完全放弃=运算符并在任何地方使用括号有什么问题?

Is it a common convention? What's the thinking behind it?

这是一个共同的惯例吗?这背后的想法是什么?

9 个解决方案

#1


4  

Unless you've proven that it matters with respect to performance, I wouldn't worry about an extra copy using the assignment operator in your example (std::string foo = "Foo";). I'd be pretty surprised if that copy even exists once you look at the optimized code, I believe that will actually call the appropriate parameterized constructor.

除非你已经证明它在性能方面很重要,否则我不会担心在你的例子中使用赋值运算符的额外副本(std :: string foo =“Foo”;)。如果你看一下优化的代码,即使存在该副本,我也会感到非常惊讶,我相信它实际上会调用适当的参数化构造函数。

In answer to your question, yes, I'd say that it's a pretty common convention. Classically, people have used assignment to initialize built-in types, and there isn't a compelling reason to change the tradition. Readability and habit are perfectly valid reasons for this convention given how little impact it has on the ultimate code.

在回答你的问题时,是的,我会说这是一个非常普遍的惯例。传统上,人们使用赋值来初始化内置类型,并没有令人信服的理由来改变传统。可读性和习惯是这个约定的完全正当理由,因为它对最终代码的影响很小。

#2


17  

Initializing variables with the = operator or with a constructor call are semantically the same, it's just a question of style. I prefer the = operator, since it reads more naturally.

使用=运算符或使用构造函数调用初始化变量在语义上是相同的,这只是样式的问题。我更喜欢=运算符,因为它更自然地读取。

Using the = operator usually does not generate an extra copy - it just calls the normal constructor. Note, however, that with non-primitive types, this is only for initializations that occur at the same time as the declarations. Compare:

使用=运算符通常不会生成额外的副本 - 它只调用普通的构造函数。但请注意,对于非基本类型,这仅适用于与声明同时发生的初始化。相比:

std::string strFooA("Foo");  // Calls std::string(const char*) constructor
std::string strFoo = "Foo";  // Calls std::string(const char*) constructor
                             // This is a valid (and standard) compiler optimization.

std::string strFoo;  // Calls std::string() default constructor
strFoo = "Foo";      // Calls std::string::operator = (const char*)

When you have non-trivial default constructors, the latter construction can be slightly more inefficient.

当你有非平凡的默认构造函数时,后一种构造可能会稍微低效。

The C++ standard, section 8.5, paragraph 14 states:

C ++标准第8.5节第14段规定:

Otherwise (i.e., for the remaining copy-initialization cases), a temporary is created. User-defined conversion sequences that can convert from the source type to the destination type or a derived class thereof are enumerated (13.3.1.4), and the best one is chosen through overload resolution (13.3). The user-defined conversion so selected is called to convert the initializer expression into a temporary, whose type is the type returned by the call of the user-defined conversion function, with the cv-qualifiers of the destination type. If the conversion cannot be done or is ambiguous, the initialization is ill-formed. The object being initialized is then direct-initialized from the temporary according to the rules above.87) In certain cases, an implementation is permitted to eliminate the temporary by initializing the object directly; see 12.2.

否则(即,对于剩余的复制初始化情况),创建临时。列举了可以从源类型转换为目标类型或其派生类的用户定义的转换序列(13.3.1.4),并且通过重载解析(13.3)选择最佳的转换序列。调用如此选择的用户定义转换,将初始化表达式转换为临时表达式,其类型是用户定义的转换函数调用返回的类型,具有目标类型的cv限定符。如果转换不能完成或不明确,则初始化是错误的。然后根据上述规则从临时对象初始化初始化对象.87)在某些情况下,允许实现通过直接初始化对象来消除临时对象;见12.2。

Part of section 12.2 states:

第12.2节的一部分规定:

Even when the creation of the temporary object is avoided, all the semantic restrictions must be respected as if the temporary object was created. [Example: even if the copy constructor is not called, all the semantic restrictions, such as accessibility (11), shall be satisfied. ]

即使在避免创建临时对象时,也必须遵守所有语义限制,就像创建临时对象一样。 [示例:即使未调用复制构造函数,也应满足所有语义限制,例如可访问性(11)。 ]

#3


11  

I just felt the need for another silly litb post.

我只是觉得需要另一个愚蠢的帖子。

string str1 = "foo";

is called copy-initialization, because what the compiler does, if it doesn't elide any temporaries, is:

被称为复制初始化,因为编译器所做的事情,如果它没有消除任何临时性,是:

string str1(string("foo")); 

beside checking that the conversion constructor used is implicit. In fact, all implicit conversions are defined by the standard in terms of copy initialization. It is said that an implicit conversion from type U to type T is valid, if

除了检查所使用的转换构造函数是否隐式之外。实际上,所有隐式转换都是由标准在复制初始化方面定义的。据说,从类型U到类型T的隐式转换是有效的,如果

T t = u; // u of type U

is valid.

In constrast,

string str1("foo");

is doing exactly what is written, and is called direct initialization. It also works with explicit constructors.

正在完成所写的内容,称为直接初始化。它也适用于显式构造函数。

By the way, you can disable eliding of temporaries by using -fno-elide-constructors:

顺便说一句,您可以使用-fno-elide-constructors禁用临时删除:

-fno-elide-constructors
    The C++ standard allows an implementation to omit creating a temporary which 
    is only used to initialize another object of the same type. Specifying this 
    option disables that optimization, and forces G++ to call the copy constructor 
    in all cases.

The Standard says there is practically no difference between

该标准表示实际上没有区别

T a = u;

and

T a(u);

if T and the type of u are primitive types. So you may use both forms. I think that it's just the style of it that makes people use the first form rather than the second.

如果T和u的类型是原始类型。所以你可以使用两种形式。我认为只是它的风格才能让人们使用第一种形式而不是第二种形式。


Some people may use the first in some situation, because they want to disambiguate the declaration:

有些人可能会在某些情况下使用第一种,因为他们想要消除声明的歧义:

T u(v(a));

migh look to someone as a definition of a variable u that is initialized using a temporary of a type v that gets a parameter for its constructor called a. But in fact, what the compiler does with that is this:

migh将某人视为变量u的定义,该变量使用类型v的临时值进行初始化,该类型为其构造函数a获取一个名为a的参数。但事实上,编译器使用的是:

T u(v a);

It creates a function declaration that takes a argument of type v, and with a parameter called a. So people do

它创建了一个函数声明,它接受类型为v的参数,并带有一个名为a的参数。所以人们这样做

T u = v(a);

to disambiguate that, even though they could have done

消除歧义,即使他们可以做到

T u((v(a)));

too, because there are never parentheses around function parameters, the compiler would read it as a variable definition instead of a function declaration too :)

因为函数参数周围没有括号,编译器会把它读作变量定义而不是函数声明:)

#4


2  

You will probably find that code such as

你可能会发现这样的代码

std::string strFoo = "Foo";

will avoid doing an extra copy and compiles to the same code (a call of a single-argument constructor) as the one with parentheses.

将避免执行额外的复制并编译为与括号相同的代码(单参数构造函数的调用)。

On the other hand, there are cases where one must use parentheses, such as a constructor member initialisation list.

另一方面,有些情况下必须使用括号,例如构造函数成员初始化列表。

I think the use of = or parentheses to construct local variables is largely a matter of personal choice.

我认为使用=或括号来构造局部变量在很大程度上取决于个人选择。

#5


1  

Well, who knows what they think, but I also prefer the = for primitive types, mainly because they are not objects, and because that's the "usual" way to initialize them.

那么,谁知道他们的想法,但我也更喜欢= for primitive类型,主要是因为它们不是对象,因为这是初始化它们的“通常”方式。

#6


0  

It's an issue of style. Even the statement that "std::string = "Foo"; would be inefficient because it would involve an extra copy" is not correct. This "extra copy" is removed by the compiler.

这是一个风格问题。甚至声明“std :: string =”Foo“;这将是低效的,因为它涉及额外的副本”是不正确的。编译器删除了这个“额外副本”。

#7


0  

I believe that is more of a habit, very few objects could be initialized using = , the string is one of them. It's also a way of doing what you said "using parenthesis everywhere (that the language allows you to use it)"

我相信这更像是一种习惯,很少有对象可以用=来初始化,字符串就是其中之一。它也是一种做你所说的“在任何地方使用括号(语言允许你使用它)”的方式

#8


0  

One argument that one could make for:

人们可以提出的一个论点:

std::string foo("bar");

Is that it keeps things the same even if the argument count changes, i.e.:

即使参数计数发生变化,它是否保持相同,即:

std::string foo("bar", 5);

std :: string foo(“bar”,5);

Doesn't work with a '=' sign.

不适用于'='符号。

Another thing is that for many objects a '=' feels unnatural, for example say you have a Array class where the argument gives the length:

另一件事是,对于许多对象,'='感觉不自然,例如说你有一个Array类,其中参数给出了长度:

Array arr = 5;

数组arr = 5;

Doesn't feel good, since we don't construct an Array with the value 5, but with length 5:

感觉不好,因为我们不构造值为5的数组,但长度为5:

Array arr(5);

feels more natural, since you are constructing an object with the given parameter, not just copying a value.

感觉更自然,因为您正在使用给定参数构造对象,而不仅仅是复制值。

#9


0  

But then just to confuse you even more you initialize primitives in the initialization list using object syntax.

但是,为了让您更加困惑,您可以使用对象语法初始化初始化列表中的基元。

foo::foo()   
  ,anInt(0)   
  ,aFloat(0.0)   
{   
}   

#1


4  

Unless you've proven that it matters with respect to performance, I wouldn't worry about an extra copy using the assignment operator in your example (std::string foo = "Foo";). I'd be pretty surprised if that copy even exists once you look at the optimized code, I believe that will actually call the appropriate parameterized constructor.

除非你已经证明它在性能方面很重要,否则我不会担心在你的例子中使用赋值运算符的额外副本(std :: string foo =“Foo”;)。如果你看一下优化的代码,即使存在该副本,我也会感到非常惊讶,我相信它实际上会调用适当的参数化构造函数。

In answer to your question, yes, I'd say that it's a pretty common convention. Classically, people have used assignment to initialize built-in types, and there isn't a compelling reason to change the tradition. Readability and habit are perfectly valid reasons for this convention given how little impact it has on the ultimate code.

在回答你的问题时,是的,我会说这是一个非常普遍的惯例。传统上,人们使用赋值来初始化内置类型,并没有令人信服的理由来改变传统。可读性和习惯是这个约定的完全正当理由,因为它对最终代码的影响很小。

#2


17  

Initializing variables with the = operator or with a constructor call are semantically the same, it's just a question of style. I prefer the = operator, since it reads more naturally.

使用=运算符或使用构造函数调用初始化变量在语义上是相同的,这只是样式的问题。我更喜欢=运算符,因为它更自然地读取。

Using the = operator usually does not generate an extra copy - it just calls the normal constructor. Note, however, that with non-primitive types, this is only for initializations that occur at the same time as the declarations. Compare:

使用=运算符通常不会生成额外的副本 - 它只调用普通的构造函数。但请注意,对于非基本类型,这仅适用于与声明同时发生的初始化。相比:

std::string strFooA("Foo");  // Calls std::string(const char*) constructor
std::string strFoo = "Foo";  // Calls std::string(const char*) constructor
                             // This is a valid (and standard) compiler optimization.

std::string strFoo;  // Calls std::string() default constructor
strFoo = "Foo";      // Calls std::string::operator = (const char*)

When you have non-trivial default constructors, the latter construction can be slightly more inefficient.

当你有非平凡的默认构造函数时,后一种构造可能会稍微低效。

The C++ standard, section 8.5, paragraph 14 states:

C ++标准第8.5节第14段规定:

Otherwise (i.e., for the remaining copy-initialization cases), a temporary is created. User-defined conversion sequences that can convert from the source type to the destination type or a derived class thereof are enumerated (13.3.1.4), and the best one is chosen through overload resolution (13.3). The user-defined conversion so selected is called to convert the initializer expression into a temporary, whose type is the type returned by the call of the user-defined conversion function, with the cv-qualifiers of the destination type. If the conversion cannot be done or is ambiguous, the initialization is ill-formed. The object being initialized is then direct-initialized from the temporary according to the rules above.87) In certain cases, an implementation is permitted to eliminate the temporary by initializing the object directly; see 12.2.

否则(即,对于剩余的复制初始化情况),创建临时。列举了可以从源类型转换为目标类型或其派生类的用户定义的转换序列(13.3.1.4),并且通过重载解析(13.3)选择最佳的转换序列。调用如此选择的用户定义转换,将初始化表达式转换为临时表达式,其类型是用户定义的转换函数调用返回的类型,具有目标类型的cv限定符。如果转换不能完成或不明确,则初始化是错误的。然后根据上述规则从临时对象初始化初始化对象.87)在某些情况下,允许实现通过直接初始化对象来消除临时对象;见12.2。

Part of section 12.2 states:

第12.2节的一部分规定:

Even when the creation of the temporary object is avoided, all the semantic restrictions must be respected as if the temporary object was created. [Example: even if the copy constructor is not called, all the semantic restrictions, such as accessibility (11), shall be satisfied. ]

即使在避免创建临时对象时,也必须遵守所有语义限制,就像创建临时对象一样。 [示例:即使未调用复制构造函数,也应满足所有语义限制,例如可访问性(11)。 ]

#3


11  

I just felt the need for another silly litb post.

我只是觉得需要另一个愚蠢的帖子。

string str1 = "foo";

is called copy-initialization, because what the compiler does, if it doesn't elide any temporaries, is:

被称为复制初始化,因为编译器所做的事情,如果它没有消除任何临时性,是:

string str1(string("foo")); 

beside checking that the conversion constructor used is implicit. In fact, all implicit conversions are defined by the standard in terms of copy initialization. It is said that an implicit conversion from type U to type T is valid, if

除了检查所使用的转换构造函数是否隐式之外。实际上,所有隐式转换都是由标准在复制初始化方面定义的。据说,从类型U到类型T的隐式转换是有效的,如果

T t = u; // u of type U

is valid.

In constrast,

string str1("foo");

is doing exactly what is written, and is called direct initialization. It also works with explicit constructors.

正在完成所写的内容,称为直接初始化。它也适用于显式构造函数。

By the way, you can disable eliding of temporaries by using -fno-elide-constructors:

顺便说一句,您可以使用-fno-elide-constructors禁用临时删除:

-fno-elide-constructors
    The C++ standard allows an implementation to omit creating a temporary which 
    is only used to initialize another object of the same type. Specifying this 
    option disables that optimization, and forces G++ to call the copy constructor 
    in all cases.

The Standard says there is practically no difference between

该标准表示实际上没有区别

T a = u;

and

T a(u);

if T and the type of u are primitive types. So you may use both forms. I think that it's just the style of it that makes people use the first form rather than the second.

如果T和u的类型是原始类型。所以你可以使用两种形式。我认为只是它的风格才能让人们使用第一种形式而不是第二种形式。


Some people may use the first in some situation, because they want to disambiguate the declaration:

有些人可能会在某些情况下使用第一种,因为他们想要消除声明的歧义:

T u(v(a));

migh look to someone as a definition of a variable u that is initialized using a temporary of a type v that gets a parameter for its constructor called a. But in fact, what the compiler does with that is this:

migh将某人视为变量u的定义,该变量使用类型v的临时值进行初始化,该类型为其构造函数a获取一个名为a的参数。但事实上,编译器使用的是:

T u(v a);

It creates a function declaration that takes a argument of type v, and with a parameter called a. So people do

它创建了一个函数声明,它接受类型为v的参数,并带有一个名为a的参数。所以人们这样做

T u = v(a);

to disambiguate that, even though they could have done

消除歧义,即使他们可以做到

T u((v(a)));

too, because there are never parentheses around function parameters, the compiler would read it as a variable definition instead of a function declaration too :)

因为函数参数周围没有括号,编译器会把它读作变量定义而不是函数声明:)

#4


2  

You will probably find that code such as

你可能会发现这样的代码

std::string strFoo = "Foo";

will avoid doing an extra copy and compiles to the same code (a call of a single-argument constructor) as the one with parentheses.

将避免执行额外的复制并编译为与括号相同的代码(单参数构造函数的调用)。

On the other hand, there are cases where one must use parentheses, such as a constructor member initialisation list.

另一方面,有些情况下必须使用括号,例如构造函数成员初始化列表。

I think the use of = or parentheses to construct local variables is largely a matter of personal choice.

我认为使用=或括号来构造局部变量在很大程度上取决于个人选择。

#5


1  

Well, who knows what they think, but I also prefer the = for primitive types, mainly because they are not objects, and because that's the "usual" way to initialize them.

那么,谁知道他们的想法,但我也更喜欢= for primitive类型,主要是因为它们不是对象,因为这是初始化它们的“通常”方式。

#6


0  

It's an issue of style. Even the statement that "std::string = "Foo"; would be inefficient because it would involve an extra copy" is not correct. This "extra copy" is removed by the compiler.

这是一个风格问题。甚至声明“std :: string =”Foo“;这将是低效的,因为它涉及额外的副本”是不正确的。编译器删除了这个“额外副本”。

#7


0  

I believe that is more of a habit, very few objects could be initialized using = , the string is one of them. It's also a way of doing what you said "using parenthesis everywhere (that the language allows you to use it)"

我相信这更像是一种习惯,很少有对象可以用=来初始化,字符串就是其中之一。它也是一种做你所说的“在任何地方使用括号(语言允许你使用它)”的方式

#8


0  

One argument that one could make for:

人们可以提出的一个论点:

std::string foo("bar");

Is that it keeps things the same even if the argument count changes, i.e.:

即使参数计数发生变化,它是否保持相同,即:

std::string foo("bar", 5);

std :: string foo(“bar”,5);

Doesn't work with a '=' sign.

不适用于'='符号。

Another thing is that for many objects a '=' feels unnatural, for example say you have a Array class where the argument gives the length:

另一件事是,对于许多对象,'='感觉不自然,例如说你有一个Array类,其中参数给出了长度:

Array arr = 5;

数组arr = 5;

Doesn't feel good, since we don't construct an Array with the value 5, but with length 5:

感觉不好,因为我们不构造值为5的数组,但长度为5:

Array arr(5);

feels more natural, since you are constructing an object with the given parameter, not just copying a value.

感觉更自然,因为您正在使用给定参数构造对象,而不仅仅是复制值。

#9


0  

But then just to confuse you even more you initialize primitives in the initialization list using object syntax.

但是,为了让您更加困惑,您可以使用对象语法初始化初始化列表中的基元。

foo::foo()   
  ,anInt(0)   
  ,aFloat(0.0)   
{   
}