简体   繁体   English

C++ 使用纯虚类和引用的多态性

[英]C++ Polymorphism using pure virtual classes and references

I get an error: cannot allocate an object of abstract type...我收到一个error: cannot allocate an object of abstract type...

for为了

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();
}

I know this is not ideal as the caller should be in charge of this.我知道这并不理想,因为调用者应该负责这个。 But this would mean to change plenty of callers in my codebase.但这意味着要更改我的代码库中的大量调用者。 This is a reproducible example:这是一个可重现的例子:

#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);
}

The reason this happens is a bit technical.发生这种情况的原因有点技术性。 You might know every expression in C++ has a value category : for object types, an lvalue names an existing object, a prvalue can be used to create an object of its type, and an xvalue is like an lvalue but allows the object to be moved from. You might know every expression in C++ has a value category : for object types, an lvalue names an existing object, a prvalue can be used to create an object of its type, and an xvalue is like an lvalue but allows the object to be moved从。

In your example, B() is a prvalue and input is an lvalue.在您的示例中, B()是纯右值, input是左值。 But the conditional expression condition? B(): input但是条件表达式condition? B(): input condition? B(): input needs to have just one value category. condition? B(): input需要只有一个值类别。 This keeps the C++ language rules consistent, plus it would be impractical to define and determine how things work if an expression sometimes names an existing object and sometimes initializes a new object.这使 C++ 语言规则保持一致,此外,如果表达式有时会命名现有的 object 并且有时会初始化新的 object,那么定义和确定事情的工作方式是不切实际的。 So the rules for the conditional operator say that if either Y or Z is a prvalue, then X? Y: Z所以条件运算符的规则说,如果YZ是纯右值,那么X? Y: Z X? Y: Z is also a prvalue. X? Y: Z也是prvalue。 So condition? B(): input那么condition? B(): input condition? B(): input is a prvalue, which means it never actually names the existing object input , but might be used to create an object initialized from input . condition? B(): input是一个纯右值,这意味着它实际上从未命名现有的 object input ,但可能用于创建从input初始化的 object 。

And a prvalue creates objects using the expression's type. prvalue 使用表达式的类型创建对象。 The type of X? Y: Z X? Y: Z X? Y: Z is the "common type" of Y and Z . X? Y: ZYZ的“共同类型”。 The common type of B and const Abstract is Abstract , since we can't guarantee that the result is a B . Bconst Abstract的常见类型是Abstract ,因为我们不能保证结果是B So the expression condition? B(): input那么表达condition? B(): input condition? B(): input could in theory be used to create an object of type Abstract . condition? B(): input来创建Abstract类型的 object。 But of course no such objects can ever be created.但当然,永远不能创建这样的对象。

Also, if we had this exact example except that class Abstract were not actually abstract, then the statement另外,如果我们有这个确切的例子,除了 class Abstract实际上不是抽象的,那么语句

const Abstract& p = condition ? B() : input;

would silently slice an object, and lose all the polymorphism!会默默地切片 object,并失去所有的多态性! With condition true, it would create a temporary B , then create a temporary Abstract by slicing that B .如果condition为真,它将创建一个临时B ,然后通过切片该B创建一个临时Abstract With condition false, it would create a temporary Abstract by slicing input .如果condition为 false,它将通过切片input创建一个临时Abstract In both cases, the binding to reference p would then extend the lifetime of the temporary Abstract object up to the } which ends the scope of p .在这两种情况下,与引用p的绑定都会将临时Abstract object 的生命周期延长到}结束p的 scope 。

So the simplest way to get around this is probably to make sure your B object expression is an lvalue:所以解决这个问题的最简单方法可能是确保你的B object 表达式是一个左值:

B b;
const Abstract& p = condition ? b : input;

Or if the default constructor of B is expensive or has undesirable side effects when it's not going to be used, maybe something like:或者,如果B的默认构造函数很昂贵或在不使用时具有不良副作用,则可能类似于:

std::optional<B> b;
const Abstract& p = condition ? b.emplace() : input;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM