2015-11-02 2 views
7

У меня есть этот пример:Почему не был вызван конструктор std :: string move?

#include <string> 
#include <iostream> 

class Test { 
private: 
    std::string str; 
public: 
    Test(std::string &&str_) : 
     str(str_) 
    {} 

    const std::string &GetStr() 
    { 
     return str; 
    } 
}; 

int main(int argc, char *argv[]) 
{ 
    std::string there("1234567890"); 
    std::cout << "1. there: " << there << '\n'; 

    Test t1(std::move(there)); 

    std::cout << "2. there: " << there << '\n'; 
    std::cout << "3. there: " << t1.GetStr() << '\n'; 
} 

Это дает выходной сигнал

$ ./a.out 
1. there: 1234567890 
2. there: 1234567890 
3. there: 1234567890 

Это использование GCC 5.1.1 на Linux. В то время как строка there остается в правильном, но неопределенном состоянии после перемещения, эта реализация, похоже, перемещает (а не копирует) строку, если вызывается конструктор перемещения std :: string.

, если я заменю initalizer str(str_) с str(std::move(str_)) я получаю этот выход:

$ ./a.out 
1. there: 1234567890 
2. there: 
3. there: 1234567890 

Это наводит на мысль двигаться конструктор станд :: строка теперь используется, но почему не std::string(std::string &&) вызывается в моем первом примере?

ответ

6

Вы должны сделать

public: 
    Test(std::string &&str_) : 
     str(std::move(str_)) 
    {} 

str_ есть имя, это именованный объект, поэтому он не будет передан какой-либо функции в качестве RValue ссылки.

Выбор дизайна, сделанный Комитетом по стандарту, предотвращает его обращение как rvalue, поэтому вы не можете случайно его изменить. В частности: тип str_ do - это ссылка lvalue на string, но str_ не считается rvalue, потому что это именованный объект.

Вы должны указать свое намерение, добавив звонок в std::move. Сделав это, вы заявляете, что хотите str_ быть rvalue, и вы знаете все последствия этого выбора.

3

Поскольку lvalue-reference всегда выигрывает! Вот почему вам нужно явно указать std::move.

Разрешается формировать ссылки на ссылки с помощью типа манипуляции в шаблонах или определениях типов, в этом случае применить ссылку разрушающихся правила: Rvalue ссылка ссылки RValue коллапсирует эталону RValue, все остальные комбинации образуют Lvalue ссылки :

typedef int& lref; 
typedef int&& rref; 
int n; 
lref& r1 = n; // type of r1 is int& 
lref&& r2 = n; // type of r2 is int& 
rref& r3 = n; // type of r3 is int& 
rref&& r4 = 1; // type of r4 is int&& 

Взятые из here.

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