Вопрос по c++11, rvalue-reference, c++ – Должен ли конструктор перемещения принимать константную или неконстантную ссылку на значение?

29

В нескольких местах я видел рекомендованные подписи конструкторов копирования и перемещения, заданные как:

struct T
{
    T();
    T(const T& other);
    T(T&& other);
};

Где конструктор копирования принимает константную ссылку, а конструктор перемещения принимает неконстантную ссылку на значение.

Однако, насколько я вижу, это мешает мне использовать семантику перемещения при возврате const-объектов из функции, как, например, в следующем примере:

T generate_t()
{
    const T t;
    return t;
}

Тестирование это с бета-версии VC11,Tвызывается конструктор копирования, а не конструктор перемещения. Даже используяreturn std::move(t); конструктор копирования все еще вызывается.

Я вижу, как это имеет смысл, так какt является константой, поэтому не должен связываться сT&&, С помощьюconst T&& в конструкторе перемещения подпись работает нормально, и имеет смысл, но тогда у вас есть проблема, потому чтоother const, вы не можете обнулять его члены, если они должны быть обнулены - это будет работать только тогда, когда все члены являются скалярами или имеют конструкторы перемещения с правильной сигнатурой.

Похоже, что единственный способ убедиться, что конструктор перемещения вызывается в общем случае, чтобы сделатьt во-первых, неконстантный, но мне не нравится это делать - созидательные вещи - это хорошая форма, и я не ожидал бы, что клиентT знать, что они должны были пойти против этой формы, чтобы увеличить производительность.

Итак, я думаю, что мой вопрос имеет два аспекта; во-первых, должен ли конструктор перемещения принимать константную или неконстантную ссылку на значение? И второе: я прав в этом рассуждении? Что я должен прекратить возвращать вещи, которые являются постоянными?

Ну, я думаю, что случаи использования возврата const temporases несколько малы. Если вы объявите это заранее, то, потому что вы хотите сделать некоторые вещи, прежде чем вернуться. Если нет, то простойreturn T() будет делать, в любом случае. Хотя я все еще вижу определенные варианты использования, я думаю, что они редки. И, конечно же, ссылка на rvalue, из которой вы не можете украсть ресурсы, на самом деле не имеет никакой ценности по сравнению с ссылкой на lvalue. Интересный вопрос. Christian Rau
consting things is good form нет, если вы хотите изменить их, например, двигаясь от них. Jonathan Wakely
Кстати, этот код не подходит для NRVO? Так что же "должно" случается так, чтоconst объектt создается в том же месте, что и неконстантное возвращаемое значение. Тогда при необходимости это неконстантное возвращаемое значениеcan быть перемещенным вызывающей стороной через неконстантный конструктор перемещения (или оператор присваивания перемещения). В случае строительства, однако, он снова будет иметь право на копирование иt может быть встроен непосредственно в то, что инициализируется с помощью вызоваgenerate_t(), Что мешает VC11 выполнить эту оптимизацию? Steve Jessop
@SteveJessop, он имеет право на копирование. Он не может быть перемещен, потому что он таков, что конструктор перемещения не является жизнеспособным, поэтому конструктор копирования выбран, но копия исключена. Если переменная была неконстантной (или конструктор перемещения занялconst T&& which would be bad) тогда конструктор перемещения будет жизнеспособным, но будет исключен. Таким образом, единственная разница должна заключаться в том, будет ли исключен ход или копия, но он должен быть исключен в любом случае. Jonathan Wakely
... в результате чего, пока кто-то не объяснит, как я не смог понять взаимодействие ходов с копиями, я считаю, что вы правы в том, что клиентT doesn't нужно идти против этой формы, чтобы повысить производительность. Однако, как проблема QoI, VC11 (с вариантами, которые вы ему дали) отбросил мяч. Тем не менее, в целом, я не думаю, что было бы правильно сказать, что клиент классаshouldn't нужно подумать о том, какие объекты можно перемещать, чтобы повысить производительность. Просто в этом случае я не думаю, что они это делают. Steve Jessop

Ваш Ответ

4   ответа
27

const Rvalue ссылка.

Если объект помещен в постоянную память, вы не можете украсть ресурсы из него, даже если его формальное время жизни скоро закончится. Объекты, созданные какconst в C ++ разрешено жить в постоянной памяти (используяconst_cast попытка изменить их приводит к неопределенному поведению).

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit ExceededconstError: User Rate Limit Exceeded
10

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

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

Насколько я понимаю, это причина того, что Скотт Мейерс изменил свой совет по возврату объектов класса по значению из функций для C ++ 11. Возврат объектов по определенному значению const предотвращает непреднамеренное изменение временного объекта, но также запрещает переход от возвращаемого значения.

Error: User Rate Limit ExceededIf it were possible to move from a const object it would usually imply that it was as efficient to copy an object as it was to "move" from it
7

Should a move constructor take a const or non-const rvalue reference?

Это должно занятьnon-const rvalue reference, Ссылки на rvalue прежде всего не имеют смысла в их константных формах просто потому, что вы хотите их изменить (таким образом, вы хотите"move" им, вы хотите их внутренности для себя).

Кроме того, они были разработаны для использованияwithout const, и я считаю, что единственное использование для ссылки на const rvalue - это нечто очень очень загадочное, о чем упоминал Скотт Мейерсэтот говорить.

Am I right in this line of reasoning? That I should stop returning things that are const?

Я считаю, что это слишком общий вопрос, чтобы ответить на него. В этом контексте я думаю, что стоит упомянуть, чтоstd::forward функциональность, которая сохранит как rvalue-ness и lvalue-ness, так и константу, а также позволит избежать создания временного элемента, как это будет делать обычная функция, если вы вернете все, что ей передано.

Это возвращение также вызвало быrvalue reference быть "изуродованным" вlvalue reference и вы, как правило, не хотите, чтобы, следовательно, совершенная пересылка с вышеупомянутой функциональностью решала проблему.

При этом я предлагаю вам просто взглянуть на доклад, на который я разместил ссылку.

1

что сказано в других ответах, иногда есть причины для конструктора перемещения или функции принятьconst T&&, Например, если вы передаете результат функции, которая возвращаетconst объект по значению для конструктора,T(const T&) будет называться вместоT(T&&) как и следовало ожидать (см. функциюg ниже).

Это причина удаления перегрузок, которые принимаютconstT&& заstd::ref and std::cref вместо тех, которые принимаютT&&.

В частности, порядок предпочтения при разрешении перегрузки следующий:

struct s {};

void f (      s&);  // #1
void f (const s&);  // #2
void f (      s&&); // #3
void f (const s&&); // #4

const s g ();
s x;
const s cx;

f (s ()); // rvalue        #3, #4, #2
f (g ()); // const rvalue  #4, #2
f (x);    // lvalue        #1, #2
f (cx);   // const lvalue  #2

УвидетьЭта статья Больше подробностей.

Похожие вопросы