Different ways of initializing an object in c++
Imagine this class:
class Entity {
public:
int x, y;
Entity() : x(0), y(0) { }
Entity(int x, int y) : x(x), y(y) { }
}
And here are multiple ways of initializing the class with what I think I know:
Entity ent1; //Uses the default constructor, so x=0 and y=0
Entity ent2(); //Uses the default constructor, so x=0 and y=0 (Not sure)
Entity ent3(1, 2); //Made constructor, so x=1 and y=2
Entity ent4 = Entity(); //Default constructor, so x=0 and y=0
Entity ent5 = Entity(2, 3); //Made constructor, so x=2 and y=3
I know that's it's possible to make an object on the heap memory, but that's not what I am looking for at this moment.
My question is, what's the difference between these ways of initializing an object?
I'm not sure which one I should use when.
Entity ent1;
The statement above uses default constructor of class Entity
.
Entity ent2();
The declaration above will be treated by compiler as a function prototype if that's possible. It's known as a case of most vexing parse (MVP) and its existence led to appearance of misleading "clever dumb rule": "never use parenthesis".
In statement like this a user-defined constructor is invoked for ent3
:
Entity ent3(1, 2);
Another case where MVP can strike is something like this:
Entity ent3_1(int(a), int(b)); // It's not what it looks like.
ent3_1
above is not a variable. The statement declares a function with two int parameters. int(a)
being same as int a
is legacy of C language and declaration syntax there.
Entity ent4 = Entity();
ent4
is a proper version of ent2
case until C++11. Default constructor is invoked as part of value initialization. Its form allows to avoid an ambiguity solving principle which makes ent2
and ent3_1
incorrect. Equal sign here is not an assignment, for no operator=
call will happen here. It's part of declaration syntax meant to markup the initialization expression.
Entity ent5 = Entity(2, 3);
ent5
is a version of ent3 case. User-defined constructor invoked as part of value initialization.
Your question is tagged as C++11, and C++11 allows uniform initialization syntax:
Entity ent12{}; // This is a legal alternative of ent2 case
Entity ent13{1, 2}; // A call to constructor or member initialization
Entity ent13{ int(a), int(b) }; // Not a function anymore
Entity ent14 = {}; // Not an assignment
Entity ent15 = Entity{2, 3}; // Not an assignment either!
Note that uniform initialization syntax has a caveat. E.g. this line
std::vector<int> v(10);
declares a vector of 10 elements. But this one
std::vector<int> v{10};
declares a vector initialized with single element of type int with value 10. This happens because std::vector
has a constructor with following signature defined:
vector( std::initializer_list<T> init, const Allocator& alloc = Allocator() );
In case that you can't use neither () without triggering MVP nor {} without invoking undesired constructor, the value initialization assignment syntax allows to resolve the issue.
Addendum: Must watch CppCon 2018: Nicolai Josuttis “The Nightmare of Initialization in C++”