Nick
Nick

Reputation: 674

difference between { } and equal sign variables

I'm somewhat new to c++ programming. I couldn't find my answer any where on google so hopefully it can be answered here.

is there a difference between the following

unsigned int counter{ 1 };

or

unsigned int counter = 1;

the book uses the first option and its confusing to me because it doesn't explain the difference. below is the following code from the book i'm following.

#include <iostream>
#include <iomanip>
#include <cstdlib> // contains function prototype for rand()
using namespace std;

int main()
{
    for (unsigned int counter{ 1 }; counter <= 20; ++counter) {
        cout << setw(10) << (1 + rand() % 6);

        // if counter is divisible by 5, start a new line of output
        if (counter % 5 == 0) {
            cout << endl;
        }
    }

}

Upvotes: 6

Views: 1358

Answers (4)

Ajay Gidwani
Ajay Gidwani

Reputation: 139

unsigned int counter = 1 ; This style of initialization is inherited from C language.

unsigned int counter {1} ; This style of initialization is of C++.

The difference is if you provide a wrong value while using C++ style initialization for example:

unsigned int counter {-1} ; This will give error (use -std=c++11 to compile it)

But this will not give any error.

unsigned int counter = -1 ;

Upvotes: 1

Vlad from Moscow
Vlad from Moscow

Reputation: 310990

Consider the following demonstrative program.

#include <iostream>

struct A
{
    int x;
    explicit A( int x = 0 ) : x( x ) {}
};

int main()
{
    A a1 { 10 };
    std::cout << "a1.x = " << a1.x << '\n';

//    A a2 = { 10 };
}

In this declaration

A a1 { 10 };

there is used the direct initialization.

And in the commented declaration

//    A a2 = { 10 };

that can be also rewritten like

//    A a2 = 10;

there is used the copy-initialization. But the constructor declared with the specifier explicit. So the compiler will issue an error. That is it is unable to convert the integer object 10 to the type of A implicitly.

You could write instead

    A a2 = A{ 10 };

That is calling the constructor explicitly.

Of course for fundamental types there is no difference because neither constructor is applied except that narrowing conversion is not allowed when the braced initialization is used as for example

int x { 1.0 };

There is a big difference when the type specifier is the placeholder auto.

For example

auto x = 10;

x has the type int.

auto x { 10 };

x again has the type int.

auto x = { 10 };

Now x has the type std::initializer_list<int>.

For example you could rewrite your loop

for (unsigned int counter{ 1 }; counter <= 20; ++counter) {

the following way

for ( auto counter{ 1u }; counter <= 20; ++counter) {

but you may not write

for ( auto counter = { 1u }; counter <= 20; ++counter) {

because in this case the type of the variable counter is std::initializer_list<unsigned int>.

So in general you have the following forms of initializations

T x = value;
T x = { value };
T x( value );
T x { value };

For example

#include <iostream>

int main()
{
    int x1 = 1;

    std::cout << "x1 = " << x1 << '\n';

    int x2 = ( 2 );

    std::cout << "x2 = " << x2 << '\n';

    int x3( 3 );

    std::cout << "x3 = " << x3 << '\n';

    int x4{ 4 };

    std::cout << "x4 = " << x4 << '\n';
}    

The program output is

x1 = 1
x2 = 2
x3 = 3
x4 = 4

But there is one more situation when instead of T() you should use T{} as an initializer. It is when template functions are used.

Consider the following demonstrative program

#include <iostream>

template <class>
void f()
{
    std::cout << "f<T>() is called\n";
}

template <int>
void f()
{
    std::cout << "f<int>() is called\n";
}

int main()
{
    f<int()>();
    f<int{}>();
}    

Its output is

f<T>() is called
f<int>() is called

The construction int() used as a template argument specifiers the type template argument int while the construction int{} used as a template argument specifiers a non-type template argument of the type int equal to 0.

Upvotes: 1

Ishaan S
Ishaan S

Reputation: 61

Adding to the other explanations above. I would use the first option(unsigned int counter{ 1 };) to initialize a list of values for a variable and for a single value, I would prefer to use the second option(unsigned int counter = 1;)

Hope this helps.

Upvotes: 1

BiagioF
BiagioF

Reputation: 9715

Yes, they are two different types of initialization in C++.

For all the details you can directly refer to the documentation.

However, I can emphasize:

Copy-initialization is less permissive than direct-initialization: explicit constructors are not converting constructors and are not considered for copy-initialization.

Initialization references here

For unsigned int type (such in your case), there are no real differences between the two initializations.


Note The usage of curly braces in the first statement (unsigned int counter{ 1 }) provides an additional constraint:

Otherwise (if T is not a class type), if the braced-init-list has only one element [...], T is direct-initialized [...], except that narrowing conversions are not allowed.

In other words, the usage of curly braces in the initialization does not allow data looseness.

That is:

unsigned int counter{ 12.3 };  // error!!!

won't compile because you are trying to initialize an integer with a floating-point value.

Note this is a "property" of curly braces in the initialization. It is not strictly related to the initialization type.

In fact, you can also write:

unsigned int counter = { 12.3 };  // error!

which is, instead, a copy initialization, but having curly braces does not allows narrowing conversions.

Upvotes: 5

Related Questions