作为std :: initializer_list对象的抽象类 [英] Abstract class as std::initializer_list object

查看:106
本文介绍了作为std :: initializer_list对象的抽象类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为了拥有更简洁的语法,我想使用 std :: initializer_list 将对象列表发送给构造函数。但是,对象是抽象的,这会引起问题:在VS 2013中,该对象失去了vfptr引用,并给出了 R6025:纯虚函数调用运行时错误,而在g ++中,它抱怨编译期间 无法分配抽象类型'base'的对象。我猜想编译器正在尝试复制对象(这是不希望的-它们可能很大),但是仅能成功复制基类,因此会出现错误。我的问题是:是否有一种解决方案(1)避免复制对象,而(2)不会过于冗长,从而消除了更干净的语法优势?下面的代码说明了我的问题:

In order to have a cleaner syntax, I would like to use an std::initializer_list to send a list of objects to a constructor. The objects, however, are abstract, which causes a problem: in VS 2013, it looses the vfptr reference, giving a "R6025: pure virtual function call" runtime error, and in g++ it complains that it "cannot allocate an object of abstract type ‘base’" during compilation. I surmise the compiler is trying to copy the objects (which is undesirable -- they may be big), but succeeds only in copying the base class, hence the error. My question is: Is there a solution which (1) avoids copying the objects and (2) isn't massively verbose, negating the "cleaner syntax" advantage? The code below illustrates my issue:

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

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

Note that using base& in the template errors since std::initializer_list tries to "[form a] pointer to reference type base&", and while using base*, and then taking the address of each derived class does in-fact work, it does so by taking the address of temporaries, and thus isn't safe (g++ complains). The latter does work if I declare the derived classes outside of the method call (my provisional solution), but it still is more verbose than I hoped for.

推荐答案

使用 initializer_list< base *> 的某种骇人听闻的方法:

Somewhat hackish approach using a initializer_list<base *>:

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

然后从通话中删除括号:

Then remove the braces from the call:

foo_everything(derived(1), derived(2), derived(3));






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


If you don't really need to convert to base * and perform a virtual call, and just want to call foo() on each object passed in, then we can use the usual pack-expansion-inside-an-initializer-list trick:

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

这篇关于作为std :: initializer_list对象的抽象类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆