What is the correct way to initialize a variable in C++
Both forms are direct initialization.
Using curly braces {}
for initialization checks for narrowing conversions and generates an error if such a conversion happens. Unlike ()
. (gcc is buggy in this regard and needs -Werror=narrowing
compiler option to generate an error when narrowing occurs.)
Another use of curly braces {}
is for uniform initialization: initialize both types with and without constructors using the same syntax, e.g.:
template<class T, class... Args>
T create(Args&&... args) {
T value{std::forward<Args>(args)...}; // <--- uniform initialization + perfect forwarding
return value;
}
struct X { int a, b; };
struct Y { Y(int, int, int); };
int main() {
auto x = create<X>(1, 2); // POD
auto y = create<Y>(1, 2, 3); // A class with a constructor.
auto z = create<int>(1); // built-in type
}
The only drawback of using curly braces {}
for initialization is its interaction with auto
keyword. auto
deduces {}
as std::initializer_list
, which is a known issue, see "Auto and braced-init-lists".
First one is the C++03 style direct initialization. The second is C++11 style direct initialization, it additionally checks for narrowing conversions. Herb Sutter recommends the following in new code:
auto c = <expression>;
or when you want to commit to specific type T:
auto c = T{<expression>};
One known drawback with curly braces when T is some class with overloaded constructor, where one constructor gets std::initializer_list as parameter, std::vector for example:
auto v = std::vector<int>{10}; // create vector<int> with one element = 10
auto v = std::vector<int>(10); // create vector<int> with 10 integer elements
Now we have five forms of initializations. They are
T x = expression;
T x = ( expression );
T x ( expression );
T x = { expression };
T x { expression };
Each of the forms has its own peculirities. :)
For example let's assume that you have the following declarations in the global namespace
int x;
void f( int x ) { ::x = x; }
int g() { return x ; }
long h() { return x; }
then in main you can write
int main()
{
int x ( g() );
}
This code will compile successfully.
However a programmer by mistake made a typo
int main()
{
int x; ( g() );
^^
}
Oops! This code also compiles successfully.:)
But if the programmer would write
int main()
{
int x = ( g() );
}
and then make a typo
int main()
{
int x; = ( g() );
^^
}
then in this case the code will not compile.
Well let's assume that the programmer decided at first to set a new value for the global variable x before initializing the local variable.
So he wrote
int main()
{
int x ( f( 10 ), g() );
}
But this code does not compile!
Let's insert equality sign
int main()
{
int x = ( f( 10 ), g() );
}
Now the code compiles successfully!
And what about braces?
Neither this code
int main()
{
int x { f( 10 ), g() };
}
nor this code
int main()
{
int x = { f( 10 ), g() };
}
compiles!:)
Now the programmer decided to use function h(), He wrote
int main()
{
int x ( h() );
}
and his code compiles successfully. But after a time he decided to use braces
int main()
{
int x { h() };
}
Oops! His compiler issues an error
error: non-constant-expression cannot be narrowed from type 'long' to 'int' in initializer list
The program decided to use type specifier auto. He tried two approaches
int main()
{
auto x { 10 };
x = 20;
}
and
int main()
{
auto x = { 10 };
x = 20;
}
and ...some compilers compiled the first program but did not compile the second program and some compilers did not compile the both programs.:)
And what about using decltype
?
For example the programmer wrote
int main()
{
int a[] = { 1, 2 };
decltype( auto ) b = a;
}
And his compiler issued an error!
But when the programmer enclosed a in parentheses like this
int main()
{
int a[] = { 1, 2 };
decltype( auto ) b = ( a );
}
the code compiled successfully!:)
Now the programmer decided to learn OOP. He wrote a simple class
struct Int
{
Int( int x = 0 ) : x( x ) {}
int x;
};
int main()
{
Int x = { 10 };
}
and his code compiles successfully.
But the programmer has known that there is function specifier explicit
and he has decided to use it
struct Int
{
explicit Int( int x = 0 ) : x( x ) {}
int x;
};
int main()
{
Int x = { 10 };
}
Oops! His compiler issued an error
error: chosen constructor is explicit in copy-initialization
The programmer decided to remove the assignment sign
struct Int
{
explicit Int( int x = 0 ) : x( x ) {}
int x;
};
int main()
{
Int x { 10 };
}
and his code compiled successfully!:)