2013-05-25 2 views
4

У меня есть следующая ситуация, когда мне нужно переместить конструкцию t2 из t1. К сожалению, это невозможно сделать (допустимо нарушение constness)
Каков правильный подход к прозрачному обращению от вызывающего пользователя foo? (То есть. Без необходимости прохождения по стоимости и явное зЬй :: перемещения)Переместить конструкцию из const reference

struct T 
{ 
    T() = default; 
    ~T() = default; 
    T(T&&) = default; 
}; 

T foo(const T& t) 
{ 
    T t3; 

    if (predicate) 
     return t3; 
    else 
     return std::move(t); 
} 

int main() 
{ 
    T t1; 
    T t2 = foo(t1); 

    return 0; 
} 
+2

Если вам нужно переместить-построить 't2' из' t1', почему бы вам не сделать 'T t2 {std :: move (t1)}'? –

+2

Какова цель 'foo'? – GManNickG

+0

См. Обновление для более конкретного примера – 3XX0

ответ

4

Вы могли бы сделать это, если бы захотели достаточно плохо. Для этого ваш foo закончится как по существу повторной реализацией std::move.

struct T 
{ 
    T() = default; 
    ~T() = default; 
    T(T&&) = default; 
}; 

template<class T> struct my_remove_reference  {typedef T type;}; 
template<class T> struct my_remove_reference<T&> {typedef T type;}; 

// The third part of a normal `remove_reference`, but which we don't need in 
// this case (though it'll all still be fine if you un-comment this). 
//template<class T> struct my_remove_reference<T&&> {typedef T type;}; 

template <typename T> 
typename my_remove_reference<T>::type &&foo(T &&t) { 
    return static_cast<typename my_remove_reference<T>::type &&>(t); 
} 

int main() 
{ 
    T t1; 
    T t2 = foo(t1); 

    return 0; 
} 

Теперь, несмотря на то, что вы можете/могли бы сделать это, другие ответы остаются более или менее правильно - даже если вы может сделать это, вы почти наверняка не должны. Если вы использовали этот foo в реальном классе, который поддерживает перемещение (т. Е. Будет действительно перемещать состояние от источника до места назначения), вы в конечном итоге уничтожаете исходный объект, хотя он остается видимым.

Выполнение этого с вашим отредактированным вопросом, где foo может, но не всегда, уничтожить исходный объект будет частности pernicious. При правильном имени, которое дает понять, что foo действительно перемещается из источника, может, возможно, просто разумно это сделать - но (по крайней мере, для меня) функция, которая может или не может уничтожить ее аргумент значительно хуже, чем тот, который делает это надежно. Очевидно, что версия foo, как и выше, что просто делает то же, что и std::move, бессмысленно, но версия, которая также выполняла некоторую другую обработку на входе (и по какой-либо причине не могла использоваться вместе с std::move) , возможно, будет едва приемлемым, если у вас действительно нет другого выбора.

+0

Спасибо за этот прекрасный ответ :) Действительно, это кажется глупым после подавления всего кода вокруг ... – 3XX0

5

Что такое правильный подход для обработки, что прозрачно от вызывающего обув?

Вы не можете этого сделать, и это должно быть так. Lvalue никогда не перемещается из прозрачного.

Поскольку перемещенный объект обычно остается в неизвестном (но законном) состоянии, было бы опасно, если бы клиент мог передать значение lvalue в качестве аргумента функции, и этой функции разрешалось молча перемещаться от него , Клиент может быть оставлен объектом зомби, даже не зная об этом!

Поэтому язык применяет правило, согласно которому перемещение из lvalue должно быть сознательным действием и должно происходить путем явного перевода значения lvalue в rvalue (фактически, xvalue) посредством вызова std::move().

В этом случае, я бы сказал, ваша функция foo() - независимо от того, что на самом деле должен делать - надо взять ссылку RValue, и клиент должен вызывать его следующим образом:

T t2 = foo(std::move(t1)); 

Обратите внимание, что это последнее предложение может быть неверным после того, как вы превратите бессмысленное имя, такое как foo(), во что-то, что отражает семантику некоторой конкретной операции в какой-то конкретной программе. Однако, не зная, в чем смысл этой операции, я могу только предоставить официальные, механические советы о том, как сделать компиляцию кода кода и как думать о семантике перемещения вообще.

3

без необходимости прохождения по значению и явное зЬй :: двигаться

C++ не позволяет перемещать вещи без по крайней мере одного из них. Это по дизайну.

Кардинальное правило движения на C++ заключается в том, что оно не может произойти случайно. Если у вас есть lvalue, и вы хотите передать его некоторым функциям, которые могут перейти от него, то вы, как вызывающий абонент , должны использовать std::move. Это по дизайну, так что ясно, что цель caller's заключается в том, что объект будет перемещен.

Таким образом, когда вы просматриваете код, вы можете увидеть, случайно ли вы использовали перемещенное значение ненадлежащим образом, не видя ничего, кроме явного использования std::move.

Что вы хотите - это не то, что вам нужно.

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