2016-03-12 3 views
0

Я пытаюсь понять, как идеальные экспедиторскую работу, но я не могу понять, почему конструктор копии вызывается в коде нижеНе может понять, почему совершенная пересылка не работает

#include <utility> 
#include <iostream> 
using std::cout; 
using std::endl; 

class Something { 
public: 
    Something() = default; 
    Something(__attribute__((unused)) const Something& other) { 
     cout << "Copy constructor called" << endl; 
    } 
    Something(__attribute__((unused)) Something&& other) { 
     cout << "Move constructor called" << endl; 
    } 

    void print() { 
     cout << "Something::print() called" << endl; 
    } 
}; 

void function_1(Something&& one) { 
    cout << "version two called" << endl; 
    Something inner{one}; 
    inner.print(); 
} 
void function_1(const Something& one) { 
    Something inner(one); 
    inner.print(); 
} 

template <typename... T> 
void test_function(T&&... ts) { 
    function_1(std::forward<T>(ts)...); 
} 

int main() { 

    const Something some1 {Something()}; 

    test_function(some1); 
    test_function(Something()); 

    return 0; 
} 

Это производит следующий вывод

Copy constructor called 
Something::print() called 
version two called 
Copy constructor called 
Something::print() called 

Изменение кода для включения std::move в справочной системе rvalue работает, но я не ожидал, что это понадобится. Когда ссылка является ссылкой rvalue, правильный конструктор следует вызывать автоматически правильно? Правильная ссылка разрешена, но вызывается неправильный конструктор. Любая помощь будет принята с благодарностью!

+1

Это должно быть легко понять: просто установите точку останова на конструкторе, и когда вы нажмете на нее, backtrace расскажет вам, как он вызван. Это одна из самых простых проблем, которые могут быть решены с помощью отладчика. –

+0

@SamVarshavchik Я понимаю, как его вызывают, но вызывает неправильную перегрузку! – Curious

+0

Что вы скажете? Вы знаете, что вам нужно явно использовать std :: move(), чтобы сохранить семантику ссылки rvalue, правильно? –

ответ

4

Ссылка на rvalue связывает с rvalues. Он сам не является rvalue, потому что у него есть имя.

Но все, что имеет имя в точке использования, является значением lvalue по умолчанию, даже ссылкой rvalue. Ваш код мог бы использовать Something&& one три раза, и если первое использование неявно move s вы были бы завинчены.

Вместо этого он является значением lvalue в точке использования (по умолчанию) и привязывается к rvalue.

Если вы хотите, чтобы сигнал больше не требовал, чтобы его состояние сохранялось, std::move.

Идеальная пересылка может использоваться для записи обоих ваших function_1 s, помещая std::forward<Blah>(blah) в точке, где вы хотите переместиться из blah, если это была ссылка rvalue.


Теперь выше полна лжи, ибо есть xvalues ​​prvalues ​​lvalues ​​и т.д. - стандарт является более сложным. Например, использование переменной в операторах return может превратить именованное значение в значение r. Но основное эмпирическое значение стоит знать: оно имеет имя, оно является значением lvalue (кроме случаев, когда оно явно литое или истекающее).

+0

Не могли бы вы объяснить: «Но все, что имеет имя в точке использования, по умолчанию равно lvalue, даже ссылки rvalue. Ваш код мог бы использовать Something && один раз три раза, и если первое использование неявно перемещается, вы будете ввернуты». немного больше? Извините, мне трудно понять это ... – Curious

+1

@curious «У меня есть что-то»? Да? Тогда это lvalue. Представьте, что вы сделали auto x = one; std :: cout << one << one; return x; '- какой из этих 3 должен двигаться? Rvalues ​​- это вещи без имени (поэтому * нельзя * повторно использовать) или вещи, явно переданные в rvalue (так что ошибки программистов) или локальные значения, возвращаемые в простой оператор return (super-NRVO). – Yakk

+0

Охх ... Это совершенно противоречит моему ранее некорректному пониманию! – Curious

1

Этот код будет называть копией ctor, а не перемещением ctor.

void function_1(Something&& one) { 
    cout << "version two called" << endl; 
    Something inner{one}; 
    inner.print(); 
} 

Этот код вызывает перемещение ctor.

void function_1(Something&& one) { 
    cout << "version two called" << endl; 
    Something inner{std::move(one)}; 
    inner.print(); 
} 

Выражение one технически L-значение. Это относится к rvalue-reference. Но чтобы получить rvalue-reference, вы должны использовать std::move. Обычно все, что имеет имя, является значением l. Без имени, как временные конструкции вашего выражения Something() в main():

test_function(Something()); 

может быть Rvalue-х и может вызвать движение без использования std::move.

+0

Но почему же вызывает вызов конструктора копирования? Я не думаю, что полностью понимаю, что именно происходит. 'one' является rvalue. Итак, почему бы не вызвать конструктор rvalue? – Curious

+1

Что происходит, 'test_function (Something()) вызывает строку' version two called'. (На первой строке реализации 'function_1 (Something &&)'). Затем происходит копирование. В 'Something inner {one};'. В 'test_function (Something())' он правильно сопоставляет это как ссылку r-value, иначе вы не увидите 'version two called'. Только когда «внутренняя» создается, вы получаете копию. –

+0

Я хотел бы отметить оба ответа как правильные, но, к сожалению, я не могу этого сделать. Я отвечу ваши ответы, чтобы дать вам эквивалентный бонус! – Curious

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