繁体   English   中英

抽象类作为std :: initializer_list对象

[英]Abstract class as std::initializer_list object

为了获得更清晰的语法,我想使用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) });
}

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

使用initializer_list<base *>一些hackish方法:

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

然后从通话中删除大括号:

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

如果你真的不需要转换为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)...};
}

如果你想要语法

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

要工作,你必须使用模板。 例如,

template<typename T>
void foo_everything(std::initializer_list<T> list)
{
  for(const auto&x:list) x.foo();
}

您可以添加一些SFINAE魔法以确保T来自基数:

template<typename T>
typename std::enable_if<is_base_of<base,T>::value>::type
foo_everything(std::initializer_list<T> list)
{
  for(const auto&x:list) x.foo();
}

如果要在一次调用foo_everything使用不同的派生类 ,则无法使用首选语法

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

(句号)这只是因为std::initializer_list包装了一个数组,并且数组必须具有相同类型的对象。 因此,从严格意义上说,答案很简单:无法做到。

暂无
暂无

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

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