Явное присвоение против неявного присвоения

Я читаю учебник для С++, но на самом деле это не давало мне разницы (помимо синтаксиса) между ними. Вот цитата из учебника.

Вы также можете присваивать значения своим переменным при объявлении. Когда мы присваивать значения переменной с помощью оператора присваивания (равно знак), его называют явным присваиванием:

int nValue = 5; // explicit assignment

Вы также можете присваивать значения переменным, используя неявное присвоение:

int nValue(5); // implicit assignment

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

Есть ли разница? Является ли еще предпочтительнее другого?

Ответ 1

Первый предпочтительнее с примитивными типами, такими как int; второй - с типами, у которых есть конструктор, потому что он вызывает явное выражение конструктора.

Например, если вы определили class Foo, который может быть создан из одного int, тогда

Foo x(5);

предпочтительнее

Foo x = 5;

(Вам нужен прежний синтаксис, когда передано более одного аргумента, если вы не используете Foo x = Foo(5, "hello");, который является уродливым и выглядит как operator=.)

Ответ 2

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

Инициализация копирования (T t = u;) эквивалентна построению копирования из временного типа T, который неявно преобразован из u в T. С другой стороны, прямая инициализация эквивалентна прямому вызову соответствующего конструктора.

В то время как в большинстве случаев не будет никакой разницы, если конструктор, который принимает u, объявлен explicit или если конструктор копирования недоступен, то инициализация копирования не будет выполнена:

struct A {
   explicit A( int ) {}
};
struct B {
   B( int ) {}
private:
   B( B const & );
};
int main() {
   A a(1);      // ok
   B b(1);      // ok
// A a2 = 1;    // error: cannot convert from int to A
// B b2 = 1;    // error: B( B const & ) is not accessible
}

Для некоторого исторического фона изначально примитивные типы должны были инициализироваться с помощью инициализации копирования. Когда * initializer-list * s были добавлены к языку для инициализации атрибутов участника из класса, было решено, что примитивные типы должны быть инициализированы с тем же синтаксисом, что классы, чтобы синтаксис в списке инициализаторов был единообразным и простым. В то же время, разрешая инициализацию классов с помощью инициализации копирования, пользовательские типы приближаются к примитивным типам. Разумеется, различия в двух форматах инициализации: int a = 5.0; обрабатывается как преобразование от 5.0 до int, а затем инициализация a из int. То же самое происходит с определенными пользователем типами: T u = v; обрабатывается как преобразование из v в T, а затем копирует конструкцию u из этого преобразованного значения.

Ответ 3

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

int nValue = 5; // assignment syntax

и

int nValue(5); // construction syntax

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

Для типов классов я предпочитаю конструктивный синтаксис, поскольку он устраняет существование функции-конструктора.