为了获得更清晰的语法,我想使用 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&amp;模板错误,因为std :: initializer_list尝试&#34; [形成]指针引用类型base&amp; &#34;,并在使用base *时,然后取每个地址派生类确实在实际工作,它通过获取临时地址来实现,因此不安全(g ++抱怨)。如果我在方法调用之外声明派生类(我的临时解决方案),后者确实有效,但它仍然比我希望的更冗长。
答案 0 :(得分:4)
使用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()
,那么我们可以使用通常的包扩展 - 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)...};
}
答案 1 :(得分:3)
如果你想要语法
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
来自base:
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
包装一个数组而一个数组必须有相同类型的对象。因此,从严格意义上讲,答案很简单:无法完成。