使用移动语义和对抽象子项的引用向量 类

Use move semantics and vector of references to children of abstract classes

我正在尝试(未成功)创建对象向量并使用多态性而不需要手动内存管理和任何类型的指针(*、unique_ptr、shared_ptr、weak_ptr , etc) 依赖于 raii 习语。

#include <iostream>
#include <vector>
using namespace std;

struct A{
    virtual void foo()=0;
    virtual ~A(){}
};
struct C:A{
    int bar;
    C(int bar):bar(bar){}
    virtual void foo(){
        cout << bar << endl;
    }
    virtual ~C(){}
};
struct B:A{
    vector<A> &&bar;
    int f;
    B(int f, vector<A> bar):f(f),bar(bar){}
    B(int f, vector<A> &bar):f(f),bar(bar){}
    B(int f, vector<A> &&bar):f(f), bar(bar){}
    virtual void foo(){
        cout << f << endl;
        for(auto &f: bar){
            f.foo();
        }
    }
    virtual ~B(){}
};

int main() {
    B b{1, vector<A>{C{1},C{2}}};
    b.foo();
    return 0;
}

但它无法编译。我做错了什么?

std::vector<A> something

是一个为A动态分配存储空间的vector,当添加object时,只有A

向量的存储类型是A,它存储A的实例。

当您将第一个 C 推到它上面时,它会执行以下操作:

void push_back(const A& a)  // NOTE: A, not C, downcasting occurs
{
    // extreme aproximation
    if (something.begin_ == nullptr) {
      something.begin_ = new A;
      something.begin_[0] = a;  // A::operator=(const A&);
      something.capacity_ = something.end_ = something.begin_ + 1;
    } else {
      ..
    }
}

因为 C 继承自 A 它可以向下转换为 this。但是,它只会复制传递给它的 object 的 A 部分,因为向量 的存储类型是 A 并且所以你在 C 中的 int bar 没有被复制。

这称为切片,绕过它的唯一方法要么是动态的(指针或 std::any),要么是使用联合的危险路径。

-- 编辑--

次要问题是您希望 std::vector<A> &&bar 做什么?它引用的临时 objects 在构造函数表达式的末尾到期:

B b{1, vector<A>{C{1},C{2}}};  // temporary vector is destroyed
// b.bar is dangling rvalue reference

-- 编辑 2 --

您的问题标题包含 "use move semantics",但如编辑 1 中所述,您仅存储右值 reference。移动语义是关于转移的:考虑这个简单的 strdup 包装器:

struct Strdupper {
    char* str_;
    Strdupper() = delete;
    Strdupper(const char* str) : str_(strdup(str)) {}
    ~Strdupper() { if (str_) free(str_); }
    Strdupper(const Strdupper& rhs) { str_ = strdup(rhs.str_); }
    Strdupper(Strdupper&& rhs) str_(rhs.str_) { rhs.str_ = nullptr; }
    Strdupper& operator=(const Strdupper& rhs) { if (str_) free(str_); str_ = strdup(rhs.str_); return *this; }
    Strdupper& operator=(Strdupper&& rhs) { std::swap(str_, rhs.str_); return *this; }
    // ...
};

您的 class B 需要实际存储向量和 std::move 输入参数值而不是右值引用,以调用它的构造函数 move-constructor:

class B {
    std::vector<X> vec_;

public:
    B() = default;
    B(const std::vector<X>& vec) : vec_(vec) {}  // copy semantics
    B(std::vector<X>&& vec) : vec_(std::move(vec)) {}  // move semantics
};