Вызов тривиального конструктора по умолчанию явно приводит к дополнительному вызову присваивания - PullRequest
0 голосов
/ 21 мая 2018

Демо

Сначала проблема: у меня есть существующая структура с конструктором по умолчанию, который переводит ее в допустимое состояние.Мне нужен конструктор NoInit, который вообще не инициализируется.Существующий код имеет несколько операторов инициализации во время объявления для переменных-членов, отсюда и пример для struct B, где я пытаюсь переопределить операторы инициализации во время объявления путем явного вызоваконструктор по умолчанию std::atomic.

std::atomic конструктор по умолчанию не инициализирует:

Конструктор по умолчанию тривиален: не происходит инициализация, кроме нулевой инициализации static и thread-local объекты.

Итак, почему вызов конструктора по умолчанию должен приводить к любому присваиванию в сборке?

struct A {
    std::atomic<long> a;
    std::atomic<long> b;
    int c;

    A() : a{1}, b{2}, c{3} {} // This is obviously fine.
    A(DefaultInit) : a(), b(), c() {} // Should this not be similar to NoInit?
    A(NoInit) {} // This is fine. Results in no code

};

Второй конструктор A(DefaultInit) приводит к присваиванию 0 длятри переменные-члены, тогда как A(NoInit) не приводит к коду вообще.

struct B {
    std::atomic<long> a{1};
    std::atomic<long> b{2};
    int c{3};

    B() {} // This is fine.
    B(NoInit) : a(), b(), c() {} // Why setting to 0? Why should it generate any assignment code at all?

};

Во втором примере struct B B(NoInit) пытается явно вызвать std::atomic::atomic(), что должно привести к отсутствию store/mov инструкция.Но это приводит к присвоению 0 всем трем переменным!

Конечно, стандарт говорит, что это неопределенное поведение, если так, то почему разница между A(DefaultInit) и A(NoInit).Я хочу, чтобы сборка B(NoInit) была такой же, как A(NoInit).

Также обратите внимание, что результирующая сборка не отличается для переменной-члена c в обоих случаях.Это не имеет отношения к std::atomic

1 Ответ

0 голосов
/ 21 мая 2018

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

B(NoInit) : a(), b(), c() {} 

Инициализатор () означает, что вы инициализируете значение каждого члена.И инициализация значения влечет за собой инициализация нуля это сначала (начиная с C ++ 11).Хотя точные обстоятельства и условия для того, когда и какой тип инициализации происходят, несколько связаны, вы можете прочитать о них в связанной статье.

В вашем конкретном случае для типа std::atomic это происходит:

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

В то время как для простого int это так:

в противном случае объект инициализируется нулями.

Что в конечном итоге учитывает все три ноля, которые вы видите.


Если вы не хотите видеть нулевую инициализацию, у вас есть далекокак я могу сказать два варианта.И то, и другое было бы небольшим использованием правил для инициализаторов:

  1. Опустить инициализатор, что проще всего.
  2. Обернуть его в тип, для которого инициализация значения вызывает пользователь, предоставленный cТо, что ничего не делает ( здесь работает , но для полной поддержки вашего исходного кода потребовалось бы дополнительное количество символов для valueless_initialization).
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...