Подтвердить что ты не робот

Явное поведение конструктора экземпляра и практическое использование

Недавний вопрос заставил меня задаться вопросом о явных конструкторах копирования. Вот пример кода, который я пытался скомпилировать в Visual Studio 2005:

struct A
{
    A() {}
    explicit A(const A &) {}
};

// #1 > Compilation error (expected behavior)
A retByValue()
{
    return A();
}

// #2 > Compiles just fine, but why ?
void passByValue(A a)
{
}

int main()
{
    A a;
    A b(a); // #3 > explicit copy construction : OK (expected behavior)
    A c = a; // #4 > implicit copy construction : KO (expected behavior)

    // Added after multiple comments : not an error according to VS 2005.
    passByValue(a);
    return 0;
}

Теперь для вопросов:

  • Разрешено ли # 2 стандартом? Если это так, то в каком разделе описывается эта ситуация?
  • Известно ли вам какое-либо практическое применение для явного конструктора копирования?

[EDIT] Я только что нашел смешную ссылку на MSDN с той же ситуацией, и таинственный комментарий от основной функции: "c копируется" (как если бы это было очевидно). Как указал Оли Чарлворт: gcc не компилирует этот код, и я считаю, что он не хочет этого делать.

4b9b3361

Ответ 1

Я считаю, что соответствующие разделы С++ 03 §12.3.1 2:

Явный конструктор строит объекты так же, как неявные конструкторы, но делает это только там, где синтаксис прямой инициализации (8.5) или где casts (5.2.9, 5.4) являются явно используется. Конструктор по умолчанию может быть явным конструктором; такой конструктор будет использоваться для выполнения инициализации по умолчанию или инициализации значения (8.5).

и § 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);

Вызов passByValue(a) включает в себя инициализацию копирования, а не прямую инициализацию, и, следовательно, должна быть ошибкой, согласно С++ 03 § 12.3.1 2.

Ответ 2

Определение passByValue ОК, потому что нет инструкции, которая копирует объект A. В определении retByValue есть, конечно, оператор return, который копирует объект A.

Ответ 3

Практическое использование до С++ 11 создания явного конструктора копии в том случае, когда оно фактически является частью класса, не подлежащего копированию.

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

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

В С++ 11 (и 14) нет необходимости делать это при использовании синтаксиса =delete, так как вы получите ошибку компилятора, даже если копируете внутри самого класса или внутри друга.

Ответ 4

Чтобы расширить ответ @MSalters (это правильно), если вы должны добавить passByValue(a); к вашей функции main(), компилятор будет должен жаловаться на это.

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