Xvalues может быть rvalues, но это не значит, что они являются временными. Продление времени жизни связано с тем, что они являются временными, а не их ценовой категорией.
Я намеренно стараюсь не знать порядок обработки операторов (таким образом, я заставляю себя писать код, который либо использует явные скобки, либо не заботится о заказе). Ваш конкретный adder
пример кода, воспроизведенный здесь, делает уход:
template <class T>
struct addable
{
friend T operator +(const T& lhs, const T& rhs)
{
return std::move(T(lhs) += rhs);
}
friend T operator +(const T& lhs, T&& rhs)
{
return std::move(T(lhs) += std::move(rhs));
}
friend T&& operator +(T&& lhs, const T& rhs)
{
return std::move(lhs += rhs);
}
friend T&& operator +(T&& lhs, T&& rhs)
{
return std::move(lhs += std::move(rhs));
}
};
Если оператор +
выполняется справа налево, затем t1 + t2 + t3
будет работать в t1 + (t2 + t3)
. t2 + t3
вызовет первую перегрузку, создав таким образом временной, таким образом получив t1 + temp
. Так как временный предпочтительнее связывается с ссылкой на r-значение, это выражение вызовет вторую перегрузку, которая будет также возвращает временную.
Однако, если оператор +
работает слева направо, вы получаете (t1 + t2) + t3
. Это дает нам temp + t1
, что вызывает проблему. Это вызовет третью перегрузку. Параметр lhs
этой функции - это T&&
, ссылка на временную. Вы возвращаете ту же ссылку. Это означает, что вы вернули ссылку на временную. Но C++ этого не знает; все, что он знает, это то, что вы возвращаете ссылку на что-то.
Это «что-то», однако, должно быть уничтожено после окончательного выражения (присваивается новая переменная, тип значения или ссылочный тип). Помните: C++ не знает, что эта функция вернет ссылку на ее первый параметр. Таким образом, он не знает, что время жизни временного переданного функциональному операнду должно быть расширено до времени жизни, в котором хранится возвращаемая ссылка.
Кстати, вот почему деревья выражений могут быть опасными с auto
и такими скрывающимися. Поскольку созданные внутренние временные записи не могут быть сохранены новыми временными или ссылками, хранящимися в разных объектах. У C++ просто нет способа сделать это.
Итак, кто прав, зависит от того, в каком порядке разрешены операторы. Однако я предпочитаю свое решение: не полагайтесь на эти углы языка и просто обходите их. Прекратите возвращать T&&
из этих перегрузок и просто переместите значение во временное. Таким образом, он гарантированно работает правильно, и вам не нужно постоянно проверять стандарт, чтобы убедиться, что ваш код работает.
Кроме того, в качестве стороннего, я бы счел его несколько грубым для оператора + фактически изменить один из параметров.
Однако, если вы настаиваете на том, чтобы знать, кто прав, это GCC. Из раздела 5.7, p1:
Аддитивные операторы + и - группа слева направо.
Да, это не должно работать.
Примечание: Visual Studio позволяет T &r2 = t1 + t2 + t3;
компилировать в качестве (очень раздражающего) языкового расширения. Вы, должно быть, получили предупреждение от него.
Я не плакат на сайте M $, просто смущенный. Я даже не использую MSVC, но я беспокоюсь о смысле моего кода. – user1095108
@ user1095108: Я добавил добавление внизу. –
Вы хорошо объяснили, но могли бы вы также указать на соответствующие разделы стандарта? Если я правильно понимаю, что возвращение ссылки rvalue к временному автоматически приводит к оборванной ссылке. – user1095108