Когда следует использовать прямую инициализацию и при инициализации копии?

Это просто предпочтение или существуют конкретные случаи, когда один необходим другому? Я имею в виду следующие варианты инициализации

T t(e); // direct initialization
T t = e; // copy initialization

Ответы

Ответ 1

Фактические имена описанных вами вещей не являются неявным и явным назначением, но:

  • Копирование-инициализация: T x = a;
  • Прямая инициализация: T x(a);

Они эквивалент не, особенно в контексте, где требуется преобразование, например, когда T имеет тип класса, а a имеет другой тип (см. комментарий Alf для примеров контекстов, которые даже не связаны с конверсией). Рассмотрим следующий код:

class Test
{
public:
    explicit Test(int i) { /* ... */ }
};

int main()
{
    Test t(0);  // OK : calls Test::Test(int)
    Test u = 0; // KO : constructor is marked explicit
}

Перефразируя стандарт (8.5/14):

  • Для прямой инициализации и инициализации копирования, где тип источника совпадает с классом назначения или производным классом, конструкторы считаются
  • Для других примеров инициализации копии, например, второй строки main в моем примере, определяемая пользователем последовательность преобразования считается. Поскольку использование конструктора Test для неявного преобразования было запрещено с помощью ключевого слова explicit, вторая строка не скомпилируется.

Ответ 2

Прямая инициализация, например

std::istringstream  stream( "blah blah" );

необходим, когда тип, о котором идет речь, здесь std::istringstream из стандартной библиотеки С++, не имеет доступного конструктора копирования.

Инициализация копирования, например

std::istringstream  stream = "blah blah";   //! NOT VALID

требуется доступный конструктор копирования, поскольку он выполняется так, как если бы временный объект был создан с правой стороны =, и как будто это временное значение используется для инициализации объявляемой переменной.

В другом направлении на С++ 98 необходим синтаксис инициализации копии, чтобы использовать инициализаторы фигурных скобок. Например, прямая инициализация не может использоваться для инициализации агрегата. Но вы можете использовать инициализацию копирования с инициализатором фигурных скобок:

#include <string>
using namespace std;

struct Answer
{
    int     nVotes;
    string  description;
};    

int main()
{
    Answer const  incorrect   = { 26, "they're the same!" };
    Answer const  correct     = { -1, "nah, they're different, actually" };
}

Таким образом, существуют значительные различия.

Я обычно предпочитаю синтаксис инициализации копирования из-за ясности. Но иногда, как показано выше, прямая инициализация, к сожалению, необходима. Некоторые люди, например. Автор текста С++ Фрэнсис Стеклоборов, вместо этого приземлился на прямую инициализацию в качестве предпочтительного синтаксиса инициализации (я не уверен, почему, это менее ясно для моих глаз и вводит проблему "самого неприятного разбора" ), и для них это необходимость копирование в некоторых случаях, что является неудачным.

Приветствия и hth.,