Подтвердить что ты не робот

Правильно инициализируя переменные в современном С++ (С++ 11 и выше), используя() или {}?

справочные страницы С++ говорят, что() для инициализации значения, {} для значения и агрегата и инициализации списка. Итак, если я просто хочу инициализацию значения, какой из них я использую?() или {}? Я спрашиваю, потому что в книге "A Tour of С++" самого Бьярне он предпочитает использовать {} даже для инициализации значения (см., Например, страницы 6 и 7), и поэтому я думал, что это хорошая практика всегда используйте {}, даже для инициализации значения. Тем не менее, я был сильно укушен следующей ошибкой в ​​последнее время. Рассмотрим следующий код.

auto p = std::make_shared<int>(3);
auto q{ p };
auto r(p);

Теперь, согласно компилятору (Visual Studio 2013), q имеет тип std::initializer_list<std::shared_ptr<int>>, что не то, что я намеревался. То, что я на самом деле предназначалось для q, на самом деле таково r, которое std::shared_ptr<int>. Поэтому в этом случае я не должен использовать {} для инициализации значения, но используйте(). Учитывая это, почему Бьярне в своей книге по-прежнему предпочитает использовать {} для инициализации значения? Например, он использует double d2{2.3} в нижней части страницы 6.

Чтобы окончательно ответить на мои вопросы, когда следует использовать() и когда следует использовать {}? И это вопрос правильности синтаксиса или вопрос хорошей практики программирования?

О, и, если возможно, просто по-английски.

EDIT: Кажется, что я немного неправильно понял инициализацию стоимости (см. Ответы ниже). Однако вышеприведенные вопросы по-прежнему сохраняются.

4b9b3361

Ответ 1

Это мое мнение.

При использовании auto в качестве спецификатора типа, он более чистый, чтобы использовать:

auto q = p;   // Type of q is same as type of p
auto r = {p}; // Type of r is std::initializer_list<...>

При использовании явного спецификатора типа лучше использовать {} вместо ().

int a{};   // Value initialized to 0
int b();   // Declares a function (the most vexing parse)

Можно использовать

int a = 0; // Value initialized to 0

Однако форма

int a{};

можно также использовать для инициализации объектов определенных пользователем типов. Например.

struct Foo
{
   int a;
   double b;
};


Foo f1 = 0;  // Not allowed.
Foo f1{};    // Zero initialized.

Ответ 2

Скотт Майерс справедливо оценивает разницу между двумя методами инициализации в своей книге Эффективный современный С++.

Он суммирует оба подхода следующим образом:

Большинство разработчиков в конечном итоге выбирают один вид разделителя как значение по умолчанию, используя другой только тогда, когда они должны. Люди с привязкой по умолчанию привлеченные их непревзойденной широтой применимости, их запрет сужения конверсий и их иммунитет к С++ досадный анализ. Такие люди понимают, что в некоторых случаях (например, создание от std::vector с заданным размером и значением начального элемента), требуются скобки. С другой стороны, круглые скобки идут толпа объявляет круглые скобки как разделитель аргументов по умолчанию. Theyre привлекли его соответствие синтаксису С++ 98 традиция, его избегание файла aut-deduced-a-std:: initializer_list проблемы и знания о том, что их создание объектов не будет непреднамеренно уклоняются конструкторами std::initializer_list. Oни согласитесь, что иногда будут выполняться только брекеты (например, при создании контейнер с определенными значениями). Не существует единого мнения о том, что либо подход лучше, чем другой, поэтому мой совет - выбрать один и применяйте его последовательно.

Ответ 3

Во-первых, кажется, что терминологический микс. У вас нет инициализации значения. Инициализация значения происходит, когда вы не предоставляете явных аргументов инициализации. int x; использует инициализацию по умолчанию, значение x будет неуказано. int x{}; использует инициализацию значения, x будет 0. int x(); объявляет функцию — поэтому {} является предпочтительным для инициализации значения.

Код, который вы указали, не использует инициализацию значения. При auto наиболее безопасным является использование инициализации копирования:

auto q = p;

Ответ 4

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

int a(2.3); // ok? a will hold the value 2, no error, maybe compiler warning
uint8_t c(256); // ok? the compiler should warn about something fishy going on

По сравнению с инициализацией скобки

int A{2.3}; // compiler error, because int can NOT hold a floating point value
double B{2.3}; // ok, double can hold this value
uint8_t C{256}; // compiler error, because 8bit is not wide enough for this number

В частности, при типичном программировании с шаблонами вы должны использовать инициализацию скобок, чтобы избежать неприятных неожиданностей, когда базовый тип делает что-то неожиданное для ваших входных значений.

Ответ 5

{} - инициализация значения, если она пуста, если не инициализация списка/агрегата.

Из черновика, 7.1.6.4 автоспециалист, 7/... Пример,

auto x1 = { 1, 2 }; // decltype(x1) is std::initializer_list<int>

Правила немного сложны для объяснения здесь (даже трудно прочитать из источника!).

Ответ 6

Трава Саттер, похоже, делает аргумент в CppCon 2014 (39:25 в разговоре) для использования инициализаторов auto и brace, например

auto x = MyType { initializers };

когда вы хотите принудить тип, для согласования слева направо в определениях:

  • Тип-вывод: auto x = getSomething()
  • Тип-принуждение: auto x = MyType { blah }
  • Пользовательские литералы auto x = "Hello, world."s
  • Объявление функции: auto f { some; commands; } -> MyType
  • Именованный Лямбда: using auto f = [=]( { some; commands; } -> MyType
  • С++ 11-стиль typedef: using AnotherType = SomeTemplate<MyTemplateArg>

Ответ 7

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