作为 std::initializer_list 对象的抽象类

And*_*der 4 c++ c++11

为了使语法更简洁,我想使用std::initializer_list将对象列表发送到构造函数。然而,这些对象是抽象的,这会导致一个问题:在 VS 2013 中,它丢失了 vfptr 引用,给出了“ R6025:纯虚函数调用”运行时错误,并且在 g++ 中它抱怨它“无法分配抽象对象在编译期间键入“base” ”。我推测编译器正在尝试复制对象(这是不可取的——它们可能很大),但只能成功复制基类,因此出现错误。我的问题是:是否有解决方案(1)避免复制对象并且(2)不是非常冗长,否定“更简洁的语法”优势?下面的代码说明了我的问题:

#include <cstdio>
#include <initializer_list>

struct base{
    virtual void foo() const = 0;
};

struct derived : public base{
    int i;
    derived(int i) : i(i) {}
    void foo() const{
        printf("bar %i", i);
    }
};

void foo_everything(const std::initializer_list<base> &list){
    for (auto i = list.begin(), iend = list.end(); i != iend; i++) i->foo();
}

int main(void){

    // Works fine
    derived d(0);
    base * base_ptr = &d;
    base_ptr->foo();    

    // Does not work fine
    foo_everything({ derived(1), derived(2), derived(3) });
}
Run Code Online (Sandbox Code Playgroud)

请注意,在模板错误中使用 base& ,因为 std::initializer_list 试图“ [form a] 指向引用类型 base& 的指针”,并且在使用 base* 时,然后获取每个派生类的地址实际上可以工作,它确实所以通过获取临时地址,因此是不安全的(g++ 抱怨)。如果我在方法调用之外声明派生类(我的临时解决方案),后者确实有效,但它仍然比我希望的更冗长。

T.C*_*.C. 5

使用一个有点hackish的方法initializer_list<base *>

template<class... Ts>
void foo_everything(Ts&&... args){
    std::initializer_list<base *> list = {&args...};
    for(auto i : list) i->foo();
}
Run Code Online (Sandbox Code Playgroud)

然后从调用中删除大括号:

foo_everything(derived(1), derived(2), derived(3));
Run Code Online (Sandbox Code Playgroud)

如果您真的不需要转换base *和执行虚拟调用,而只想调用foo()传入的每个对象,那么我们可以使用通常的 pack-expansion-inside-an-initializer-list 技巧:

template<class... Ts>
void foo_everything(Ts&&... args){
    using expander = int[];
    (void) expander { 0, ((void) std::forward<Ts>(args).foo(), 0)...};
}
Run Code Online (Sandbox Code Playgroud)