Результат тройного оператора не rvalue

Если вы скомпилируете эту программу с помощью компилятора С++ 11, вектор не будет удален из функции.

#include <vector>
using namespace std;
vector<int> create(bool cond) {
    vector<int> a(1);
    vector<int> b(2);

    return cond ? a : b;
}
int main() {
    vector<int> v = create(true);
    return 0;
}

Если вы возвращаете экземпляр, подобный этому, он перемещается.

if(cond) return a;
else return b;

Вот демон на ideone.

Я попробовал его с gcc 4.7.0 и MSVC10. Оба ведут себя одинаково.
Мое предположение, почему это происходит, это:
Тип тернарных операторов - это значение lvalue, потому что оно вычисляется до выполнения оператора return. На данный момент a и b еще не имеют значений x (скоро истечет).
Правильно ли это объяснение?

Является ли это дефектом в стандарте?
Это явно не предполагаемое поведение и очень распространенный случай, на мой взгляд.

Ответ 1

Вот соответствующие стандартные кавычки:

12.8 пункт 32:

Копирование разрешений разрешено в следующих случаях: [...]

  • в выражении return в функции с типом возвращаемого класса, когда выражение является именем энергонезависимого автоматического объекта (кроме функции или параметра catch-clause) с тем же CV-неквалифицированным типом, что и возвращаемый тип функции, операцию копирования/перемещения можно опустить, построив автоматический объект непосредственно в возвращаемое значение функции
  • [when throw ing, с условиями]
  • [когда источник является временным, с условиями]
  • [при catch по значению, с условиями]

пункт 33:

Когда критерии для выполнения операции копирования выполняются или выполняются, за исключением того факта, что исходный объект является параметром функции, а подлежащий копированию объект определяется значением lvalue, разрешением перегрузки, чтобы выбрать конструктор для копия сначала выполняется так, как если бы объект был обозначен rvalue. Если сбой при перегрузке или если тип первого параметра выбранного конструктора не является ссылкой rvalue на тип объекта (возможно, cv-qualified), разрешение перегрузки выполняется снова, считая объект как lvalue. [Примечание. Это двухступенчатое разрешение перегрузки должно выполняться независимо от того, произойдет ли копирование. Он определяет вызывающий конструктор, если elision не выполняется, и выбранный конструктор должен быть доступен, даже если вызов отменяется. - конечная нота]

Так как выражение в return (cond ? a : b); не является простым именем переменной, оно не подходит для копирования или элитной обработки. Может быть, немного неудачно, но легко представить, как растянуть пример немного дальше, пока вы не создадите головную боль ожидания для реализаций компилятора.

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

Ответ 2

Это исправит его

return cond ? std::move(a) : std::move(b);

Рассмотрим тройной оператор как функцию, например, ваш код

return ternary(cond, a, b);

Параметры не будут перемещены неявно, вам нужно сделать это явным.