C ++ Полиморфизм с использованием чистых виртуальных классов и ссылок - PullRequest
0 голосов
/ 09 апреля 2020

Я получаю error: cannot allocate an object of abstract type ...

за

void foo(const Abstract& input)
{
    // just an example, this condition is returned by another function
    bool condition = true;
    // Key point: I want this decision to be done inside.
    const Abstract& p = condition ? B() : input;
    p.f();
}

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

#include <iostream>

struct Abstract
{
    virtual void f() const = 0;
};

struct A: public Abstract
{
    void f() const { std::cout << "Aaa" << std::endl;}
};

struct B: public Abstract
{
    void f() const { std::cout << "VBB" << std::endl;}
};

void foo(const Abstract& input)
{
    // just an example, this condition is returned by another function
    bool condition = true;
    // Key point: I want this decision to be done inside.
    const Abstract& p = condition ? B() : input;
    p.f();
}

int main()
{
  A a;
  foo (a);
}

1 Ответ

1 голос
/ 09 апреля 2020

Причина, по которой это происходит, немного техническая. Возможно, вы знаете, что каждое выражение в 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;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...