In C++, there are subtle semantic differences between creating objects using () and {}. The differences mainly lie in the initialization rules and applicable scenarios.
1. Using () to Initialize Objects
This method is known as function-style initialization or parentheses initialization, used for constructor calls or type conversions.
#include <iostream>
#include <vector>
int main() { int a(42); // Calls the constructor of int, equivalent to int a = 42; std::vector<int> vec(5, 10); // Creates a vector with 5 elements, each initialized to 10
std::cout << a << "\n"; // Outputs 42 for (int val : vec) { std::cout << val << " "; // Outputs 10 10 10 10 10 }
return 0;}
2. Using {} to Initialize Objects
This method is known as list initialization or uniform initialization, which has been more recommended since the introduction of C++11.
#include <iostream>
#include <vector>
int main() { int a{42}; // List initialization, equivalent to int a = 42; std::vector<int> vec{5, 10}; // Creates a vector with two elements {5, 10}
std::cout << a << "\n"; // Outputs 42 for (int val : vec) { std::cout << val << " "; // Outputs 5 10 }
return 0;}
Type Narrowing Detection
int x = 2.5; // Implicit conversion succeeds, x == 2
int y{2.5}; // Error! Compiler rejects implicit narrowing conversion
3. Comparing the Differences Between {} and ()
(1) For user-defined types, the behavior of () and {} may differ in constructor calls.
#include <iostream>
struct MyClass { MyClass(int a, int b) { std::cout << "Constructor: " << a + b << "\n"; } MyClass(std::initializer_list<int> list) { std::cout << "Initializer List: "; for (int v : list) std::cout << v << " "; std::cout << "\n"; }};
int main() { MyClass obj1(1, 2); // Calls the constructor MyClass obj2{1, 2}; // Calls the initializer list constructor (if it exists)
return 0;}
Output:
Constructor: 3
Initializer List: 1 2
() prioritizes calling the regular constructor.
{} prioritizes calling the std::initializer_list constructor (if it exists).
(2) Initializing Built-in Arrays
{} can be used directly to initialize arrays, while () cannot:
int arr1[3] = {1, 2, 3}; // OK
int arr2[3] = (1, 2, 3); // Error! Cannot use () to initialize an array
(3) Most Vexing Parse Problem
The “most vexing parse problem” refers to the situation where () initialization may be misinterpreted by the compiler as a function declaration.
struct MyClass { MyClass(int, int) {};
int main() { MyClass obj1(1, 2); // OK, calls the constructor MyClass obj2(); // Note! This is parsed as a function declaration, not an object MyClass obj3{}; // OK, creates an object return 0;}
Analysis:
-
MyClass obj2();is parsed as a declaration of a functionobj2that returnsMyClass. -
{}avoids this issue and is recommended for use.
Best Practices
-
Prefer
{}initialization: Safer, prevents implicit type narrowing, and has more uniform syntax. -
Avoid the “most vexing parse” problem: When constructing objects, prefer using
{}over(). -
Be clear about constructor behavior: When user types support
initializer_listand regular constructors, be aware of the calling priority between{}and().