Причина, по которой это происходит, немного техническая. Возможно, вы знаете, что каждое выражение в C ++ имеет категорию значений : для типов объектов lvalue присваивает имя существующему объекту, prvalue может использоваться для создания объекта его типа, а xvalue похожа на lvalue, но позволяет перемещать объект из.
В вашем примере B()
- это значение, а input
- это значение. Но условное выражение condition ? B() : input
должно иметь только одну категорию значений. Это сохраняет согласованность правил языка C ++, и было бы непрактично определять и определять, как все работает, если выражение иногда именует существующий объект, а иногда инициализирует новый объект. Так что правила для условного оператора говорят, что если Y
или Z
является prvalue, то X ? Y : Z
также является prvalue. Таким образом, condition ? B() : input
является prvalue, что означает, что он фактически никогда не называет существующий объект input
, но может использоваться для создания объекта, инициализированного из input
.
И prvalue создает объекты, используя тип выражения , Тип X ? Y : Z
является «общим типом» Y
и Z
. Общий тип B
и const Abstract
равен Abstract
, поскольку мы не можем гарантировать, что результатом будет B
. Таким образом, выражение condition ? B() : input
теоретически может быть использовано для создания объекта типа Abstract
. Но, конечно, такие объекты никогда не могут быть созданы.
Кроме того, если бы у нас был этот точный пример, за исключением того, что класс Abstract
на самом деле не был абстрактным, то выражение
const Abstract& p = condition ? B() : input;
могло бы молча нарезать объект и потерять весь полиморфизм! Если condition
true, это создаст временную B
, а затем создаст временную Abstract
, нарезав эту B
. При condition
false будет создан временный Abstract
путем нарезки input
. В обоих случаях привязка к ссылке p
продлила бы время жизни временного Abstract
объекта до }
, что заканчивает область действия p
.
Так что самый простой способ получить вероятно, чтобы убедиться, что ваше B
выражение объекта является lvalue:
B b;
const Abstract& p = condition ? b : input;
Или если конструктор по умолчанию B
дорогой или имеет нежелательные побочные эффекты, когда он не будет использоваться, может быть что-то вроде:
std::optional<B> b;
const Abstract& p = condition ? b.emplace() : input;