我在开源项目中找到的代码基本上是这样的:
template< typename... Args >
void expand_calls_hack(Args&&... args)
{}
template <unsigned int... Indices>
struct foo
{
static void bar(some_tuple_type& t)
{
meta::expand_calls_hack((std::get<Indices>(t).doSomething(), 0)...);
}
};
我认为这是&#34;构建&#34;用于为每个元组元素调用doSomething()
。但是,在我看来,调用doSomething()
的顺序是未定义的,至少它与C ++ 03中的普通函数一致。这将是一个错误,因为调用有副作用。我有两个问题:
(tupleElement.doSomething(),0)有什么用 - 是逗号运算符吗?我知道它与使用expand_calls_hack扩展调用有关。
如何解决此问题,以便从左到右评估呼叫?请注意,我需要在VC2013上编译。我已经尝试扩展lambda列表并按顺序调用它,但我无法编译它。
我希望我没有遗漏太多的内容,但对于好奇,这段代码的来源是on github here, line 419
答案 0 :(得分:2)
问题的第一部分非常简单:std::get<I>(t).doSomething()
可能会返回void
。因此,您无法直接将此表达式用作expand_calls_hack()
的参数。即使doSomething()
返回void
,使用逗号运算符也会安排参数出现。
调用可变参数函数模板时函数求值的顺序并不特殊,即评估顺序未定义。但是,事实证明,在使用大括号初始化时,构造函数参数的评估顺序是定义的(参见,例如,this question)。也就是说,您可以使用以下内容保证评估顺序:
namespace meta {
struct order {
template <typename... T>
order(T&&...) {}
};
}
// ...
template <unsigned int... Indices>
struct foo
{
template <typename T>
static void bar(T&& t)
{
meta::expand_calls_hack((std::get<Indices>(t).doSomething(), 0)...);
meta::order{(std::get<Indices>(t).doSomething(), 0)...};
}
};
在上面的函数中,expand_calls_hack()
的使用会在我的系统上回溯表达式,而order
的使用会根据需要从前到后对它们进行评估。我认为使用std::initializer_list<int>
可以实现类似的效果,这需要不需要可变参数模板:参数都是int
s无论如何:
namespace meta {
struct init_order {
init_order(std::initializer_list<int>) {}
};
}
它与meta::order
完全相同。