为了有更清晰的语法,我想使用std::初始化列表将对象列表发送到构造函数。然而,这些对象是抽象的,这会导致一个问题:在 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& 会出现错误,因为 std::initializer_list 尝试“[形成] 指向引用类型基数的指针&”,在使用 base* 时,然后获取每个派生类的地址实际上是有效的,它是通过获取临时变量的地址来实现的,因此不安全(g++ 抱怨)。如果我这样做,后者确实可以工作在方法调用之外声明派生类(我的临时解决方案),但它仍然比我希望的更详细。
使用有点黑客的方法initializer_list<base *>
:
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)...};
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)