2014-10-07 4 views
2

Следующего код, составленный на VS2013, не вызывает перемещение конструктора StD :: струны (проверяется с помощью установки контрольных точек, сопзИте конструктор иой копии вызывается вместо.Почему следующий код не вызывает конструктор перемещения std :: string?

#include <iostream> 
#include <string> 
#include <stdlib.h> /* srand, rand */ 
#include <time.h> /* time */ 

struct foo 
{ 
    foo(std::string& str1, std::string& str2) : _str1(str1), _str2(str2) {} 

    ~foo() { std::cout << "Either \"" << _str1 << "\" or \"" << _str2 << "\" was returned." << std::endl; } 

    std::string& _str1; 
    std::string& _str2; 
}; 

std::string foobar() 
{ 
    std::string str1("Hello, World!"); 
    std::string str2("Goodbye, cruel World."); 
    foo f(str1, str2); 

    srand(time(NULL)); 

    return (rand() % 2) ? str1 : str2; 
} 

int main() 
{ 
    std::cout << "\"" << foobar() << "\" was actually returned." << std::endl; 

    return EXIT_SUCCESS; 
} 

Я хотел бы ожидать заявление возвращения в Foobar() для вызова конструктора перемещения, так как я возвращаю локальный (rand() предназначен для предотвращения NRVO), как указано в качестве ответов на такие вопросы, как Returning std::move of a local variable

Контекст этого заключается в том, что я пытаюсь добавить еще один пример для моего другого вопроса здесь: https://softwareengineering.stackexchange.com/questions/258238/move-semantics-in-c-move-return-of-local-variables

+0

ли использовать конструктор перемещения, если вы удалите 'Foo' определение переменной? Я бы предположил, что компилятор достаточно умен, чтобы знать, что вы сделали ссылку на строки, что делает движение недействительным (опять же, предположение). – uesp

+0

Нет, он по-прежнему использует конструктор копирования, если я это сделаю. – Bwmat

+1

«Пробовать rvalue first» для возвратов относится только «если критерии соответствия копии выполнены или будут выполнены, за исключением того факта, что исходный объект является параметром функции». В C++ 14 он распространяется на все случаи, когда оператор return напрямую ссылается на локальный объект. (См. [Этот ответ] (http://stackoverflow.com/questions/25875596/can-returning-a-local-variable-by-value-in-c11-14-result-in-the-return-value-b/25876175 # 25876175) для полной цитаты из стандарта.) Ваше условное выражение не является ни тем, ни другим. –

ответ

5

C++ 11 имеет специальный случай, позволяющий копировать/перемещать эллипс, когда он является локальной переменной, и используется как выражение возврата из функции:

C++ 11 12.8/31 «Копирование и перемещение объектов класса» :

в ответном заявлении в функции с типом возвращаемого класса, когда выражение этого именем энергонезависимого автоматического объекта (кроме параметра функции или поймать-положение) с теми же сортами -unqualified Тип как функция типа возврата, операция копирования/перемещения может быть , опущенная путем создания автоматического объекта непосредственно в Возвращаемое значение функции

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

Позже стандарт упоминает, что

C++ 11 12,8/32 «Копирование и перемещение объектов класса»:

Когда критерии элизии операции копирования выполняются или будет выполненный с сохранением того факта, что исходный объект является параметром функции, и объект, подлежащий копированию, обозначается значением lvalue, перегрузка разрешение для выбора конструктора для копии сначала выполняется , как если бы объект был обозначен rvalue , Если сбой перегрузки или если тип первого параметра выбранного конструктора не является ссылкой rvalue на тип объекта (возможно, cv-qualified), разрешение перегрузки выполняется снова, учитывая объект как lvalue. [Примечание: Это двухступенчатое разрешение перегрузки должно быть выполнено , независимо от того, произойдет ли копирование. Он определяет вызывающий конструктор, если elision не выполняется, и выбранный конструктор должен быть доступен, даже если вызов удален. - конечная нота]

Это позволяет использовать операцию перемещения, даже если возврат указывает значение lvalue. Однако этот специальный случай применяется только в условиях первого предложения, которые не выполняются в случае вашего примера return.

Вы можете форсировать:

return (rand() % 2) ? std::move(str1) : std::move(str2); 
+0

Будет ли он работать с 'if (rand() & 2) return str1; else return str2; '? –

+0

@GuilhermeBernal: да, это тоже работает. –

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