Constructor of type int
Considering the cost, are these cases the same?
// case 1
int a = 5;
// case 2
int a (5);
// case 3
int a;
a = 5
The three syntaxes are different, bear with me while I use a user defined type instead of int, I will go back to int later.
T a(5); // Direct initialization
T b = 5; // Implicit conversion (5->tmp) + copy-initialization
T c; c = 5; // Default initialization + assignment
In the first case the object a
is constructed by means of a constructor that takes an int
or a type that can be implicitly converted from int
.
struct T {
T( int ); // T a(5) will call this directly
};
In the second case, a temporary object of type T
is created by an implicit conversion from int
, and then that temporary is used to copy construct b
. The compiler is allowed to optimize the code away and perform just the implicit conversion in place of the final object (instead of using it to create the temporary. But all restrictions have to be verified:
class T {
T( T const & );
public:
explicit implicit T( int );
};
int main() {
T b = 5; // Error 1: No implicit conversion from int to T.
// Fix: remove the `explicit` from the constructor
// Error 2: Copy constructor is not accessible
}
The third case is default construction followed by assignment. The requirements on the type are that it can be default constructed (there is a constructor with no arguments, or there is no user defined constructor at all and the compiler will implicitly define it). The type must be assignable from int
or there must be an implicit conversion from int
to a type U
that can be assigned to T
. As you see, the requirements for the type in the tree cases differ.
Besides the semantics of the different operations, there is other important difference, not all of them can be used in all of the contexts. In particular, in an initialization list in a class you cannot use the implicit convert + copy initialize version, and you can only have the first half of default construct + assign.
// OK // error // ok but different
struct test { struct test { struct test {
T x; T x; T x;
test(int v) : x(v) {} test(int v) : x=5 {} test( int v ) {
x = v;
}
In the first case the attribute x
is directly initialized with the value v
. The second case is a syntax error. The third case first default initializes and then assigns inside the body of the constructor.
Going back to the int
example, all of the requirements are met by the type, so there is almost no difference on the code that the compiler generates for the three cases, but still you cannot use the int b = 5;
version inside an initializer list to initialize an integer attribute. Moreover, if a class has a member attribute that is a constant integer, then you cannot use the equivalent of int c; c =5;
(third column above) as the member attribute becomes const
when it enters the constructor block, that is, x = v;
above would be trying to modify a constant and the compiler will complain.
As to the cost that each one has, if they can be used at all, they incur the same cost for an int
(for any POD type) but not so for user defined types that have a default constructor, in which case T c; c = 5;
will incur the cost of default construction followed by the cost of assignment. In the other two cases, the standard explicitly states that the compiler is allowed to generate the exact same code (once the constraints are checked).
First and second are exactly same as both are initialization. Third one is different, as this is assignment. These differences are as per the C++ Standard. However, the compiler can treat all three as same!
For the first two, there will be no difference.
From Standard docs, 8.5.11,
The form of initialization (using parentheses or =) is generally insignificant, but does matter when the initializer or the entity being initialized has a class type; see below. A parenthesized initializer can be a list of expressions only when the entity being initialized has a class type.
The third one is not an initialization but an assignment.
And considering the cost,
In the first two cases, you are creating an integer with a value 5.
In the third case, you are creating an integer with an undefined value and replace it with 5..
If you use an optimizing compiler, they will all compile to the same code. So they all have the same cost.