2010-11-11 2 views
35

Недавний вопрос заставил меня задаться вопросом о явных конструкторах копирования. Вот пример кода, который я пытался компилировать под 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 с точно такой же ситуацией, и загадочный комментарий от основной функции: «с скопирован» (как если бы это было очевидно). Как указал Оли Чарлворт: gcc не компилирует этот код, и я считаю, что он прав.

+0

Я не считаю, что явные конструкторы копирования - хорошая идея. Где вы читали о них? – fredoverflow

+1

Это, похоже, исправлено в VC++ 2010 - это дает ошибку для строки 'passByValue (a);'. – user200783

ответ

39

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

An explicit constructor constructs objects just like non-explicit constructors, but does so only where the direct-initialization syntax (8.5) or where casts (5.2.9 , 5.4) are explicitly used. A default constructor may be an explicit constructor; such a constructor will be used to perform default-initialization or value-initialization (8.5).

и § 8.5 12:

The initialization that occurs in argument passing, function return, throwing an exception (15.1), handling an exception (15.3), and brace-enclosed initializer lists (8.5.1) is called copy-initialization and is equivalent to the form

T x = a; 

The initialization that occurs in new expressions (5.3.4), static_cast expressions (5.2.9), functional notation type conversions (5.2.3), and base and member initializers (12.6.2) is called direct-initialization and is equivalent to the form

T x(a); 

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

+2

Хотя ответы MSalters и Oli верны, я соглашусь с этим, потому что ясно, что VS ошибается. Благодаря ! – icecrime

+0

Очень хороший ответ, спасибо. – ttvd

4

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

+1

Я не уверен, что понимаю ваш ответ.Чтобы быть ясным: я сохранил код до минимума, но для компилятора нормально использовать 'passByValue (A())' из основного, что, по моему мнению, требует неявного копирования. – icecrime

+1

@icecrime: Нет, вы не можете вызвать 'passByValue()' like this ... –

+0

Мое понимание этого состоит в том, что объект 'a' никогда не используется в' passByValue() ', поэтому компилятор его игнорирует. – djeidot

2

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

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

+0

Нет, это не будет, passByValue (a); в main() компилируется отлично, и я, как icecrime, считаю, что есть неявное копирование объекта. – davidnr

+0

@ davidnr, hmm, не удалось в gcc по причине, упомянутой выше. – Nim

+0

@davidnr: Под GCC я получаю 'error: никакой подходящей функции для вызова в A A (A &) ',' error: initializing argument 1 of' void passByValue (A) '' –

3

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

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

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

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

Смежные вопросы