5

пожалуйста, посмотрите на следующий пример кода:Почему вместо конструктора перемещения вызывается экземпляр конструктора?

#include <iostream> 

    struct Foo { 
     Foo() { std::cout << "Default!\n"; } 
     Foo(const Foo& foo) { std::cout << "Copy!\n"; } 
     Foo(Foo&& foo) { std::cout << "Move!\n"; } 
    }; 

    struct Bar { 
     Foo foo; 
     Bar() {} 
     Bar(Bar &that) : foo(that.foo) {} 
     Bar(Bar &&that) : foo(std::move(that.foo)) {} 
    }; 

    Bar f() { 
     Bar bar; 
     return bar; 
    } 

    int main() { 
     Bar bar(f()); 
    } 

I'am ожидает, что выход из этого кода должно быть:

Default! 
Move! 

но то, что я получаю:

Default! 
Copy! 

Я не вижу причин, по которым вместо конструктора перемещения вызывается конструктор копирования. Если я поставлю ключевое слово const перед Bar &that в объявлении конструктора копирования struct Bar, у меня есть правильный результат. Я знаю, что во многих случаях лучше взять ссылку на константу lvalue, а не только ссылку lvalue для конструкторов копирования, но я просто хочу знать причину, почему это произошло.

Почему в этом примере Bar & был предпочтительнее, чем Bar &&, хотя возвращаемое значение f() следует рассматривать как prvalue? Почему ключевое слово const решает проблему? const действительно решает проблему? Связано ли это с RVO (Оптимизация возвращаемого значения)? Или это просто ошибка компилятора?

Я проверил этот пример на Visual C++ November 2012 CTP.

Я нашел аналогичный вопрос здесь:

Copy constructor is being called instead of the move constructor

Но я до сих пор не могу понять, почему.

Может ли кто-нибудь мне помочь?

+0

Невозможно воспроизвести. С GCC я просто получаю 'Default!' (Sane NRVO на работе), с '-fno-elide-constructors' я получаю ожидаемый' Default! Переехать! Move! '. Может быть, компилятор b^Hshortcoming? –

+0

изменить 'Bar (Bar & the)' '' Bar (const Bar & that) '. –

+0

@KerrekSB 1. Я понимаю случай NRVO. Но почему ожидаются два, а не только один «Move!», Когда разрешение копирования отключено? 2. Вы говорите, что это всего лишь ошибка компилятора, который я использовал? Спасибо в любом случае. –

ответ

3

Wow, когда я компилирую это с ...

  1. Visual Studio в Debug я вижу "по умолчанию! Скопируй!".
  2. Visual Studio в выпуске Я вижу «По умолчанию!».
  3. Если вы изменили Bar(Bar &that) на Bar(const Bar &that), тогда «По умолчанию! Move!»
  4. Потрясающе, если вы переключите порядок Bar(Bar &that) на Bar(Bar &&that) (так, чтобы сначала было определено движение ctor), вы увидите «По умолчанию! Перемещение!».
+1

Ничего себе! Я также тестировал 1, 2 и 3 себя, но 4 действительно удивительно! Спасибо. Итак, есть ли у вас какое-либо представление о том, почему такие вещи происходят? –

+2

ОК, # 4 довольно убедительно устанавливает, что это ошибка компилятора. –

+0

Чтобы быть справедливым, 'f()' должен возвращать 'std :: move (bar)'. – Mohammad

-2

Возможно, ответ на ваш вопрос here.

Временный объект не может связываться с неконстантной ссылкой. Конструктор копирования должен ссылаться на объект const, чтобы иметь возможность делать копии временных объектов.

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

+0

Эта цитата не имеет ничего общего с наблюдаемым поведением. –

+1

@ T.C .: Эта цитата имеет все, что с ней связано. Мало того, что конструктор копирования будет хуже, он не должен даже быть частью набора кандидатов. –

+1

@BenVoigt Конечно, но это ничего не объясняет. Вопрос в том, «почему его называли?», А не «почему его не называли?». –

4

Это просто обычное несоблюдение Visual C++, позволяющее привязывать ссылку на константу без ссылки на временную.Он нарушает языковые правила, но он слишком затормозился до того, как его поймали, поэтому теперь есть код, который зависит от ошибки, которая сломалась бы, если бы она была исправлена.

Это поведение, ошибочно использующее конструктор копирования не const, который будет использоваться в сочетании с неполной поддержкой ссылок rvalue в этой версии Visual C++, по-видимому, приводит к выбору неправильной перегрузки.

Если вы хотите использовать возможности C++ 11/C++ 14, вам лучше всего оставаться на вершине последней версии Visual C++.

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