Why use = to initialise a primitive type in C++?

落花浮王杯 提交于 2019-12-05 22:05:36

问题


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:

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?

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


回答1:


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.

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:


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:

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.

Part of section 12.2 states:

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. ]




回答3:


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

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
    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.


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:

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

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:


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:


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.




回答6:


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.




回答7:


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:


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);

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 arr = 5;

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

Array arr(5);

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




回答9:


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)   
{   
}   


来源:https://stackoverflow.com/questions/353632/why-use-to-initialise-a-primitive-type-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!