2014-10-19 5 views
3

У меня есть следующий код:Почему конструктор копирования не вызывается, когда объект передается функции

#include <iostream> 
using namespace std; 

    class foo 
    { 
     public: 
      foo(int a, int b) : 
       a_(a), b_(b) 
      { cout << "regular const" << endl; } 

      foo(const foo& f) : 
       a_(f.a_), b_(f.b_) 
      { cout << "copy const" << endl; } 

      foo& operator=(const foo& rhs) 
      {cout << "copy= const" << endl; a_ = rhs.a_; b_ = rhs.b_; return *this; } 

      int a_, b_; 
    }; 

    void bar(foo f) 
    { } 

    int main() 
    { 
     foo f1(10, 20); 
     cout << "------" << endl; 

     bar(f1); 
     cout << "------" << endl; 

     bar(foo(11, 22));   // line 29 
     cout << "------" << endl; 

     bar({13, 23});    // line 32 
     cout << "------" << endl;  

    } 

я получаю следующий результат:

$ ./a.out 
regular const 
------ 
copy const 
------ 
regular const 
------ 
regular const 
------ 

Для линии 29 и линии 32, я ожидал временный объект, который должен быть создан в главном (вызов обычного конструктора), а затем вызывается конструктор копирования при передаче в bar(). Из вывода я вижу, что компилятор делает некоторую оптимизацию и угадывает, возможно, просто создает объект в стеке при вызове bar() и вызывается только обычный конструктор. Может кто-то, пожалуйста, помогите мне понять, какой тип оптимизации делается или что происходит под капотом.

Позволяет ли строка вызова() использовать строки 29 и 32 эквивалента до сгенерированного кода? Я понимаю, что строка 29 более читаема.

Я изменил бар() следующим образом:

void bar(const foo& f) 
{ } 

я получаю тот же результат для линий 29 и 32. В этом случае, когда объект создаваемой?

Спасибо, Ахмед.

+0

копирования список инициализация и копирование элизия не строят временные на стороне вызова, то же самое с возвращением –

+1

http://en.cppreference.com/w/cpp/language/copy_elision – Mat

+0

@Mat благодарственного вы за упоминание «копия элиции», я раньше не сталкивался с этой терминологией. И для предоставления ссылки. –

ответ

3

В строке 32,

bar({13, 23}); 

параметр инициализируется в копирования списка инициализации - никакого промежуточного временного не создается, даже теоретически.

bar(foo(11, 22)); 

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

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

Если вы хотите видеть результат без каких-либо скопированных копий или перемещений, используйте -fno-elide-constructors с GCC. Он будет производить ожидаемый результат. По крайней мере, для линии 32.

Теперь второй версии бара:

void bar(const foo& f) 

В строке 29, временно создается и инициализируется с рамно-Init-лист. В строке 32 ссылка привязана к временному аргументу. Никаких оптимизаций или исключений не требуется.

bar({13, 23}); // A temporary object is created as if by foo{13, 23}, 
        // and the reference is bound to it 

bar(foo(11, 22)); // The reference is bound to the temporary. 
        // The lifetime of the temporary has been extended. 
+0

Для первой версии bar() сгенерированный код - насколько не создается временный объект и местоположение созданного объекта параметра в баре, являются ли они одинаковыми. Строка 29 и строка 32? Как насчет новой версии bar()? –

+0

@AhmedA Да, после копирования, обе линии здесь эквивалентны. Они также эквивалентны для новой версии 'bar'. – Columbo

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