разница между {} и переменными знака равенства - PullRequest
6 голосов
/ 19 сентября 2019

Я немного новичок в программировании на С ++.Я не смог найти свой ответ в Google, так что, надеюсь, на него можно ответить здесь.

есть ли разница между следующими

unsigned int counter{ 1 };

или

unsigned int counter = 1;

книга использует первый вариант, и это сбивает меня с толку, потому что это не объясняет разницу.ниже приведен следующий код из книги, которой я следую.

#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;
        }
    }

}

Ответы [ 4 ]

5 голосов
/ 19 сентября 2019

Да, это два разных типа инициализации в C ++.

За всеми подробностями вы можете обратиться непосредственно к документации.

Однако могу подчеркнуть:

Копирование-инициализация менее разрешительна, чем прямая инициализация:явные конструкторы не являются конвертирующими конструкторами и не рассматриваются для инициализации копирования.

Ссылки на инициализацию здесь

Для типа unsigned int (например, в вашем случае) нет реальной разницы между двумя инициализациями.


Примечание Использование фигурных скобок в первом операторе (unsigned int counter{ 1 }) обеспечивает дополнительное ограничение:

В противном случае (, если T не являетсятип класса ), если список фигурных скобок имеет только один элемент [...], T равен с прямой инициализацией [...], за исключением того, что сужающие преобразования не являютсяпозволено .

Другими словами, использование фигурных скобок при инициализации не допускает разболтанности данных.

То есть:

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

не скомпилируется, потому что вы пытаетесь инициализировать целое число значением с плавающей точкой.

Обратите внимание, что это "свойство"фигурных скобок в инициализации. Он не связан строго с типом инициализации .

На самом деле вы также можете написать:

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

, что вместо этого означает копирование инициализации, но наличие фигурных скобок не позволяет сужать преобразования.

1 голос
/ 19 сентября 2019

unsigned int counter = 1 ; Этот стиль инициализации унаследован от языка C.

unsigned int counter {1} ; Этот стиль инициализации имеет C ++.

Разница в том, что при вводе неправильного значения вы используетеИнициализация стиля C ++, например:

unsigned int counter {-1} ; Это даст ошибку (используйте -std=c++11 для компиляции)

Но это не даст никакой ошибки.

unsigned int counter = -1 ;

1 голос
/ 19 сентября 2019

Рассмотрим следующую демонстрационную программу.

#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 };
}

В этом объявлении

A a1 { 10 };

используется прямая инициализация.

И в закомментированном объявлении

//    A a2 = { 10 };

, который также может быть переписан как

//    A a2 = 10;

, там используется инициализация копирования.Но конструктор объявлен со спецификатором explicit.Таким образом, компилятор выдаст ошибку.То есть он не может преобразовать целочисленный объект 10 в тип A неявно.

Вместо этого можно написать

    A a2 = A{ 10 };

То есть вызов конструктора явно.

Конечно, для фундаментальных типов нет никакой разницы, потому что ни один конструктор не применяется, за исключением того, что суженное преобразование не допускается, когда используется фигурная инициализация, как, например,

int x { 1.0 };

Существует большая разница, когдаспецификатор типа является заполнителем auto.

Например,

auto x = 10;

x имеет тип int.

auto x { 10 };

x снова имееттип int.

auto x = { 10 };

Теперь x имеет тип std::initializer_list<int>.

Например, вы можете переписать ваш цикл

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

следующимway

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

но вы не можете писать

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

, потому что в этом случае тип переменной counter равен std::initializer_list<unsigned int>.

Так что в общему вас есть следующие формы инициализации

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

Например

#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';
}    

Вывод программы:

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

Но есть еще одна ситуация, когда вместо T() вы должны использовать T{} в качестве инициализатора.Это когда используются шаблонные функции.

Рассмотрим следующую демонстрационную программу

#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{}>();
}    

Ее вывод

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

Конструкция int(), используемая в качестве аргумента шаблоназадает аргумент шаблона типа int, а конструкция int{} используется в качестве аргумента шаблона; специфицирует аргумент шаблона нетипичного типа int, равный 0.

0 голосов
/ 19 сентября 2019

Добавление к другим объяснениям выше.Я бы использовал первую опцию (unsigned int counter {1};), чтобы инициализировать список значений для переменной и для одного значения, я бы предпочел использовать вторую опцию (unsigned int counter = 1;)

Надеюсь, это поможет.

...