Разница между S () и S {}? - PullRequest
       61

Разница между S () и S {}?

7 голосов
/ 24 января 2020

В приведенном ниже коде, есть ли разница между двумя присвоениями для значения? В обоих случаях значение value.v будет создано по умолчанию, а значение x будет инициализировано 42?

struct S
{
   std::vector<int> v;
   int x = 42;
};
S value;

void foo()
{
   value = S();
   value = { };
}

1 Ответ

13 голосов
/ 24 января 2020

S() и S{} означают одно и то же почти во всех случаях. Но не все случаи.

  • Если S не тип класса, то же самое: инициализация значения.
  • Если S является типом класса, который является , а не агрегатом, в большинстве случаев это означает то же самое: инициализация значения. За исключением случаев, таких как:

    struct X { X(std::initializer_list<int>); };
    auto x1 = X(); // ill-formed
    auto x2 = X{}; // ok, calls constructor
    
  • , если S является агрегатом, то S() является инициализацией значения, но S{} агрегированием-инициализацией. Даже это означает одно и то же много времени. Но не всегда.

Пример 1: явный конструктор по умолчанию делает агрегатную инициализацию некорректной

struct A { explicit A(int = 0); };
struct B { A a; };

B b;        // OK
B b2 = B(); // OK
B b3{};     // error through trying to copy-list-initialize a = {}

Пример 2: инициализация значения в некоторых контекстах сначала выполняет нулевую инициализацию

struct C { C() {} int i; };
struct D { C a; }; 

D d1{};     // d1.a.i is indeterminate
D d2 = D(); // d2.a.i is zero

В примере OP, хотя S является агрегатом с неявно определенным конструктором по умолчанию - это интересный случай. Но здесь нет никаких изменений в семантике с дополнительной инициализацией нуля, мы инициализируем x в 42 и строим по умолчанию v в любом случае.

Обратите внимание, что также в OP это вызывает (и предназначено для вызова) оператор присваивания перемещения из S{}:

value = { };

также возможно, что это может вызвать другой оператор полностью, так как {} может в конечном итоге связать «лучше» с каким-то другим параметром в другой перегрузке оператора присваивания. std::optional должен перепрыгнуть через несколько хуков, чтобы opt = {} действительно вызывал оператор присваивания перемещения.

...