Есть ли отличная (или стандартная функция) с противоположным эффектом на `std:: move`

Во-первых, этот вопрос не является дубликатом Функция double to std:: move? или Существует ли инверсия std:: move?. Я не спрашиваю о механизме предотвращения перемещения в ситуации, когда он иначе имел бы место, и вместо этого копировать; скорее я задаю вопрос о механизме принятия значения r в позиции, которая будет привязана к модифицируемой справочной ссылке lvalue. Это на самом деле точная противоположность ситуации, для которой был изобретен std::move (а именно, принятие модифицируемой lvalue принимается в положение, которое будет связано с (модифицируемой) ссылкой rvalue).

В ситуации, которая меня интересует, значение rvalue не будет принято, потому что для контекста требуется модифицируемая ссылка lvalue. По какой-то причине я не совсем понимаю, но готов принять, выражение (изменчивое) rvalue будет связываться с константной ссылкой lvalue (без введения дополнительного временного), но оно не будет связываться с модифицируемой ссылкой lvalue ( сообщение об ошибке, которое gcc дает мне, - это "недопустимая инициализация неконстантной ссылки типа" A & from rvalue типа "A", в то время как clang говорит, что "ссылка не-const lvalue на тип" A "не может связываться с временным типом" А ", любопытно, я не могу заставить ни одного из этих компиляторов признать, что рассматриваемое выражение имеет тип" A & & ", даже если это выражение действительно имеет вид static_cast<A&&>(...), который сам по себе не вызывает ошибки). Я могу понять, что обычно не хотелось бы принимать выражение rvalue в позиции, требующей модифицируемой ссылки lvalue, поскольку она подразумевает, что любые изменения, сделанные с помощью этой ссылки lvalue, будут потеряны, но так же, как вызов std::move говорит компилятору" Я знаю, что это lvalue, которая будет привязана к ссылке rvalue (параметр), и, следовательно, может быть украдена, но я знаю, что я делаю, и все в порядке. "Я бы хотел сказать в моем случае", знайте, что это временно, что связано с изменяемой ссылкой на значение lvalue (параметр), и поэтому любые изменения, которые будут сделаны с помощью ссылки lvalue, исчезнут незамеченными, но я знаю, что делаю, и здесь все хорошо ".

Я могу решить проблему, инициализируя именованный объект типа A из rvalue, а затем предоставляя имя, где требуется модифицируемая ссылка на lvalue. Я не думаю, что для этого есть лишние накладные расходы во время выполнения (во всяком случае, для rvalue требовалось временное), но делать это неудобно несколькими способами: нужно ввести фиктивное имя, возможно, просто ввести составную инструкцию просто чтобы удерживать объявление, отделяя выражение, выражающее rvalue от вызова функции, оно предоставляет аргумент. Откуда мой вопрос, можно ли это сделать без введения фиктивного имени:

  • Есть ли какой-либо способ (например, использование литья) связать выражение rvalue типа A с модифицируемым ссылочным номером типа A & без ввода именованного объекта типа A?
  • Если нет, это преднамеренный выбор? (и если да, то почему?) Если есть, существует ли механизм, аналогичный std::move, предоставленный стандартом, чтобы облегчить его?

Вот упрощенная иллюстрация, где мне понадобится такое преобразование. Я намеренно удалил специальные конструкторы A, чтобы убедиться, что сообщение об ошибке не связано с временными параметрами, которые компилятор решил ввести. Все ошибки исчезают, когда A& заменяются на const A&.

class A
{ int n;
public:
  A(int n) : n(n) {}
  A(const A&) = delete; // no copying
  A(const A&&) = delete; // no moving either
  int value() const { return n; }
};

int f(A& x) { return x.value(); }

void g()
{ A& aref0 = A(4); // error
  // exact same error with "= static_cast<A&&>(A(4))" instead of A(4)
  A& aref1 = static_cast<A&>(A(5)); // error
  // exact same error with "= static_cast<A&&>(A(5))" instead of A(5)
  f (A(6)); //error
  // exact same error with "= static_cast<A&&>(A(6))" instead of A(6)

  A a(7);
  f(a); // this works
  A& aref2 = a; // this works too, of course
}

Для тех, кто задается вопросом, зачем мне это нужно, здесь один случай использования. У меня есть функция f с параметром, который служит в качестве входного аргумента, а иногда и в качестве выходного аргумента, заменяя предоставленное значение на "более специализированное" значение (значение представляет древовидную структуру и некоторые отсутствующие ветки могут быть заполнены в); поэтому это значение передается как модифицируемая ссылочная позиция. У меня также есть некоторые глобальные переменные, содержащие значения, которые иногда используются для предоставления значения для этого аргумента; эти значения неизменны, поскольку они уже полностью специализированы. Несмотря на эту постоянную природу, я не декларировал эти переменные const, так как это сделало бы их непригодными в качестве аргумента. Но они действительно считаются глобальными и постоянными константами, поэтому я хотел переписать мой код, чтобы сделать это явным, а также избежать случайного возникновения ошибок при изменении реализации f (например, он может решить перейдите из своего аргумента при вызове исключения, это будет ОК, когда аргумент представляет локальную переменную, которая в любом случае будет уничтожена исключением, но будет катастрофической, если она будет привязана к глобальной "константе" ). Поэтому я решил сделать копию при передаче одной из этих глобальных констант в f. Существует функция copy, которая создает и возвращает такую ​​копию, и я хотел бы поместить ее в качестве аргумента в f; alas copy(c) является rvalue, это невозможно сделать по причинам, описанным выше, хотя это использование совершенно безопасно и на самом деле безопаснее моего предыдущего решения.

Ответы

Ответ 1

Самое простое решение:

template<typename T>
T& force(T&& t){
   return t;
}

Ответ 2

Функция, представленная ниже, является плохой идеей. Не используйте его. Это обеспечивает очень легкий путь к оборванным ссылкам. Я бы рассмотрел код, который нуждается в нем, и действовать соответствующим образом.

Тем не менее, я думаю, что это интересное упражнение по какой-то причине, поэтому я не могу не показать его.

Внутри функции имена ее аргументов являются lvalues, даже если аргументы являются ссылками rvalue. Вы можете использовать это свойство, чтобы вернуть аргумент как ссылку lvalue.

template <typename T>
constexpr T& as_lvalue(T&& t) {
    return t;
};

Ответ 3

Оставьте прекрасную отправку из функции пересылки, и у вас есть no_move:

template<class T> constexpr T& no_move(T&& x) {return x;}

Просто убедитесь, что вы притворяетесь, что у вас есть ссылка lvalue вместо ссылки rvalue в порядке, поскольку это обходит защиту от привязки временных ссылок к неконстантным ссылкам и тому подобное.

Любой код, использующий эту функцию, почти наверняка является дефектным по дизайну.

В вашем примере прецедента правильный способ изменит тип аргумента f() на const&, поэтому нет такого адаптера, и с помощью const_cast добавить в кеш сохраненных вычислений. < ш > Обязательно не изменяйте элементы mutable не на mutable на объявленном объекте const.

Ответ 4

Это, похоже, работает во всех ваших случаях:

template <class T>
constexpr typename std::remove_reference<T>::type& copy(T&& t) {
    return t;
};

Это точно как std::move, за исключением того, что вместо этого возвращает ссылку lvalue.