Стандарт содержит некоторые сложные правила, касающиеся того, как оценивается условное выражение ([expr.cond]).Но вместо того, чтобы привести эти правила в кавычки, я собираюсь объяснить, как вы должны думать о них.
Результатом условного выражения может быть lvalue, xvalue или prvalue.Но какой из них это должен быть известен во время компиляции.(Категория значений выражения никогда не может зависеть от того, что происходит во время выполнения).Легко видеть, что если и второе, и третье выражения являются lvalue одного и того же типа, то результат также может быть сделан lvalue, и копирование не требуется.Если и второе, и третье выражения являются значениями одного и того же типа, то, начиная с C ++ 17, копирование также не должно происходить - значение типа T
представляет отложенную инициализацию объекта типа * 1004.* и компилятор просто выбирает, основываясь на условии, какое из этих двух значений будет передано в конечном итоге для инициализации объекта.
Но когда одно выражение является значением l, а другое - значениемтого же типа, то результат должен быть prvalue.Если в стандарте сказано, что результатом будет lvalue, это будет нелогично, так как условие может привести к выбору операнда prvalue, и вы не сможете преобразовать prvalue в lvalue.Но вы можете сделать это наоборот.Таким образом, стандарт говорит, что когда один операнд является lvalue, а другой - prvalue того же типа, тогда lvalue должен подвергаться преобразованию lvalue в rvalue.И если вы попытаетесь преобразовать значение lvalue в rvalue для объекта std::ostream
, программа будет плохо сформирована, так как конструктор копирования удален.
Таким образом:
- InA, оба операнда являются значениями типа prvalue, поэтому нет преобразования lvalue в rvalue;это нормально в C ++ 17 (но не в C ++ 14).
- В B преобразование lvalue-to-rvalue необходимо для
o
, поэтому компиляция не производится. - В C
oRef
является lvalue, поэтому преобразование lvalue в rvalue по-прежнему требуется, поэтому оно также не будет компилироваться. - В D,
oRRef
по-прежнему является lvalue (поскольку имя ссылки на rvalue является lvalue). - В E один аргумент является prvalue, а другой - xvalue.Xvalue по-прежнему необходимо преобразовать в prvalue, чтобы сделать результат prvalue.
Случай E заслуживает некоторых дополнительных замечаний.В C ++ 11 было ясно, что если один аргумент является значением xvalue, а другой является prvalue одного и того же типа, xvalue должен пройти (ошибочно названное) преобразование lvalue-в-значение, чтобы получить значение prvalue.В случае std::ostream
используется конструктор защищенного перемещения (поэтому программа нарушает контроль доступа членов).В C ++ 17 можно было бы подумать об изменении правила, чтобы вместо преобразования значения xvalue в значение prvalue значение prvalue материализовалось для получения значения xvalue, устраняя необходимость в перемещении.Но это изменение не имеет очевидной выгоды, и сомнительно, является ли это наиболее разумным поведением, поэтому, вероятно, поэтому оно и не было сделано (если оно даже рассматривалось).