Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 705
Tommy Herbert Avatar asked Dec 09 '08 17:12

Tommy Herbert


People also ask

Why use primitive data types?

Primitive Data Type: In Java, the primitive data types are the predefined data types of Java. They specify the size and type of any standard values. Java has 8 primitive data types namely byte, short, int, long, float, double, char and boolean.

What is a primitive type in C?

Primitive is the most fundamental data type usable in the Programming language. There are eight primitive data types: Boolean, byte, character, short, int, long, float, and double. In a Programming language, these data types serve as the foundation for data manipulation.

What makes a primitive data type primitive?

In JavaScript, a primitive (primitive value, primitive data type) is data that is not an object and has no methods or properties.

What does primitive type mean?

Primitive type refers to a whole host of less complex variables and data types in different technologies and programming syntax systems. Some of these are defined by whether the variable needs substructures, or how simple the data type is to represent.


2 Answers

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

like image 193
Adam Rosenfield Avatar answered Sep 22 '22 11:09

Adam Rosenfield


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

like image 25
Johannes Schaub - litb Avatar answered Sep 20 '22 11:09

Johannes Schaub - litb