2013-12-08 4 views
8

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

#include <iostream> 

class X 
{ 
    public: 
    X(int) { std::cout << "Construct" << std::endl; } 

    // Having a public copy constructor will make the little program 
    // compile, but not showing the side effect "Copy". 

    private: 
    X(const X&) { std::cout << "Copy" << std::endl; } 

    private: 
    X& operator = (const X&); 
}; 

int main() { 
    X x = 1; 
    return 0; 
} 
+2

X x = 1 означает X x (X (1)), насколько я знаю, но он оптимизирован до X x (1); – odinthenerd

+3

Требуется, чтобы код C++ был переносимым между реализациями, которые могут или не могут выполнять копирование по своему усмотрению. – jrok

+1

Попробуйте выполнить компиляцию с флагом '-fno-elide-constructors'. –

ответ

4

Вот соответствующие биты стандарта С ++, которые участвуют:

[dcl.init]/16, пуля 6, к югу от пули 1: Если инициализация является прямой инициализации, или если он является инициализацией копирования, где cv-неквалифицированная версия типа источника является тем же классом, что или производным классом класса назначения, конструкторы считаются ... Если конструктор не применяется или разрешение перегрузки неоднозначный, инициализация плохо сформирована. [курсив]

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

[class.copy]/31: При соблюдении определенных критериев, реализация может опустить для копирования/перемещения конструкции класса объект, даже если конструктор копирования/перемещения и/или деструктор для объекта имеют побочные эффекты ... Это разрешение операций копирования/перемещения, называемое копированием, разрешено в следующих случаях (которые могут быть объединены для устранения нескольких копий):

bullet 3: если объект временного класса, который не был привязан к ссылке (12.2), будет скопирован/перемещен в объект класса с тем же CV-неквалифицированным типом, операция копирования/перемещения может быть опущена путем создания временного объекта d прямо в цель пропущенной копии/перемещения

2

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

int main() { 
    X x(X(1)); 
    return 0; 
} 

Или более явно:

int main() { 
    X intermediate(1); 
    X x(intermediate); 
    return 0; 
} 

Таким образом, вместо того, чтобы использовать operator=, он знает, что вы пытаетесь инициализировать объект с момента объявления. По сути, компилятор «умный». Наконец, она оптимизирует этот раз к этому шагу:

int main() { 
    X x(1); 
    return 0; 
} 

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

EDIT

Для полноты, обратите внимание, что если вы попытаетесь это сделать:

int main() { 
    X x = 1; 
    x = 2; 
    return 0; 
} 

Вы увидите вопрос с частной operator=. В явном виде важно заметить, что operator= никогда не используется в исходном вопросе инициализации выше, хотя в коде появляется =.

+0

Упс должен был пойти на ответ, а не комментировать.) Поздравляю. – odinthenerd

+0

@PorkyBrain: Ваш комментарий выше касается моей озабоченности здесь, если 'X x (X (1))' оптимизирован для 'X x (1)', спасибо – RageD

+0

Код здесь не использует 'operator =' вообще. 'X x = 1' - прямая инициализация' x' и не включает присвоение; он может (но не обязательно) вызвать конструктор копирования. –

2

Вы инициализации оптимизируется компилятором вниз:

X x(1) 

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

От стандартной секции 12.8 C++ 03:

При соблюдении определенных критериев, реализация может пропустить строительство копии объекта класса, даже если конструктор копирования и/или деструктор объекта имеет побочные эффекты. В таких случаях реализация рассматривает источник и цель пропущенной копии операции как просто два разных способа обращения к одному и тому же объекту , а разрушение этого объекта происходит в более позднем из раз, когда два объекты были бы уничтожены без оптимизации . 111). Это исключение операций копирования разрешено в следующих случаях (которые могут быть объединены для устранения нескольких копий ): - in areurnurnatement в функции с возвратным типом класса, , когда выражение является именем не- нестабильный автоматический объект с тем же самым неквалифицированным типом, что и возвращаемый тип функции, операция копирования может быть опущена путем создания автоматического объекта непосредственно в возвращаемом значении функции - когда объект временного класса>, который не был привязан к ссылка (12.2) будут скопированы на объект класса с тем же CV-неквалифицированным типа, операция копирования может быть опущен путем построения темп-окон- чательно объект непосредственно в мишень пропущенной копии

Второй случай это то, что мы имеем здесь.

+1

Это не задание; это инициализация. –

8

Вы использовали так называемую «инициализацию копирования» (определенную в [decl.init]). Определенное значение состоит в том, чтобы создать временный объект типа X с использованием конструктора int, а затем инициализировать x из временного, используя конструктор копирования.

Тем не менее, стандарт также позволяет оптимизировать, называемый «разрешение конструктора копирования» (определенный в [class.copy]) в этом случае. Если эта оптимизация применяется, временного нет. x построен с использованием конструктора int так же, как если бы вы написали так называемую «прямую инициализацию» X x(1);.

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

В C++ 11 рассматриваются конструкторы перемещения, и они также имеют право на элицию. Однако этот класс X не имеет конструктора перемещения, поэтому C++ 11 и C++ 03 для этого примера одинаковы.

+2

Короче говоря, семантика диктует копию, и поэтому перед ее оптимизацией программа должна соответствовать семантике. –

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