Различаются ли эти два синтаксиса инициализатора C ++ в семантике? - PullRequest
6 голосов
/ 21 февраля 2011

Предположим, что следующий код является допустимым кодом, который правильно компилируется, что T является именем типа, а x является именем переменной.

Синтаксис один:

T a(x);

Синтаксис два:

T a = x;

Отличается ли точная семантика этих двух выражений? Если да, то при каких обстоятельствах?

Если эти два выражения когда-либо имеют разную семантику, мне также очень интересно, какая часть стандарта говорит об этом.

Кроме того, если есть особый случай, когда T - это имя скалярного типа (aka, int, long, double и т. Д.), Каковы различия, когда T является скаляром тип против нескалярного типа?

Ответы [ 5 ]

3 голосов
/ 21 февраля 2011

Да.Если тип x не T, то второй пример расширяется до T a = T(x).Это требует, чтобы T(T const&) было публичным.В первом примере не вызывается конструктор копирования.

После проверки доступности, копию можно удалить (как указал Тони).Однако его нельзя устранить до проверки доступности.

2 голосов
/ 21 февраля 2011

T a(x) это прямая инициализация и T a = x это копирование инициализации .

Из стандарта:

8.5.11 Форма инициализации (с использованием скобок или =), как правило, незначительна, но имеет значение, когда инициализируемая сущность имеет тип класса; увидеть ниже. Инициализатор в скобках может быть списком выражений, только если инициализируемая сущность имеет тип класса.

8.5.12 Инициализация, которая происходит при передаче аргумента, возврате функции, генерации исключения (15.1), обработке исключения (15.3) и заключенных в скобки списках инициализатора (8.5.1), называется копией-инициализацией и эквивалентна к форме

   T x = a;

Инициализация, которая происходит в новых выражениях (5.3.4), выражениях static_cast (5.2.9), преобразованиях типов функциональных обозначений (5.2.3) и инициализаторах базы и члена (12.6.2), называется прямой инициализацией и эквивалентно форме

    T x(a);

Разница в том, что при инициализации копирования создается временный объект, который затем используется для прямой инициализации. Компилятору разрешено избегать создания временного объекта:

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

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

2 голосов
/ 21 февраля 2011

Разница здесь между неявной и явной конструкцией, и может быть разница.

Представьте, что у вас есть тип Array с конструктором Array(size_t length), и где-то еще у вас есть функция count_elements(const Array& array).Их цель легко понятна, и код кажется достаточно читабельным, пока вы не поймете, что он позволит вам вызвать count_elements(2000).Это не только некрасивый код, но и бесполезное выделение в памяти массива 2000 элементов.

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

То, что вы хотите здесь сделать, - это объявить Array(size_t length) и явный конструктор.Это отключит неявные преобразования, и Array a = 2000 больше не будет допустимым синтаксисом.

Это был только один пример.Как только вы поймете, что делает ключевое слово explicit, легко придумать других.

2 голосов
/ 21 февраля 2011

Начиная с 8.5.14 (выделено мной):

Выбранная функция вызывается с выражением инициализатора в качестве аргумента;если функция является конструктором, вызов инициализирует временный объект целевого типа.Результат вызова (который является временным для случая конструктора) затем используется для прямой инициализации, в соответствии с приведенными выше правилами, объекта, который является местом назначения инициализации копирования.В некоторых случаях реализация позволяет исключить копирование, присущее этой прямой инициализации, путем создания промежуточного результата непосредственно в инициализируемый объект;см. class.teven, class.copy.

Итак, то, являются ли они эквивалентными, оставлено для реализации.

8.5.11 также имеет значение, но только для подтверждения того, что может быть разницей:

-11- Форма инициализации (с использованием скобок или =), как правило, незначительна, но имеет значение, когда инициализируемая сущность имеет тип класса;увидеть ниже.Инициализатор в скобках может быть списком выражений, только если инициализируемая сущность имеет тип класса.

0 голосов
/ 21 февраля 2011

В C ++, когда вы пишете это:

class A {
  public:
  A() { ... }
};

Компилятор фактически генерирует это в зависимости от того, что использует ваш код:

class A {
  public:
  A() { ... }
  ~A() { ... }
  A(const A& other) {...}
  A& operator=(const A& other) { ... }
};

Так что теперь вы можете увидеть различные семантикиразличных конструкторов.

A a1; // default constructor
A a2(a1); // copy constructor
a2 = a1; // copy assignment operator

Конструкторы копирования в основном копируют все нестатические данные.Они генерируются, только если результирующий код является допустимым и нормальным: если компилятор видит типы внутри класса, которые он не знает, как копировать (в соответствии с обычными правилами присваивания), тогда конструктор копирования не будет сгенерирован.Это означает, что если тип T не поддерживает конструкторы или если одно из открытых полей класса является, например, const или ссылочным типом, генератор не будет их создавать - и код не будет собираться.Шаблоны расширяются во время сборки, поэтому, если полученный код не будет собран, произойдет сбой.И иногда это происходит громко и очень загадочно.

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

...