可变参数模板扩展中的函数调用顺序
Order of function calls in variadic template expansion
我在一个开源项目中找到的代码基本上是这样的:
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)...);
}
};
我认为这个 "construct" 用于为每个元组元素调用 doSomething()
。然而,在我看来,对 doSomething()
的调用顺序是未定义的,至少在 C++03 中是这样的。这将是一个错误,因为调用有副作用。我有两个问题:
(tupleElement.doSomething(), 0) 有什么用 - 那是逗号运算符吗?我认为这与使用 expand_calls_hack 扩展调用有关。
如何解决这个问题,以便从左到右计算调用?请注意,我需要在 VC2013 上进行编译。我试过扩展 lambda 列表并按顺序调用它,但我无法编译它。
我希望我没有遗漏太多上下文,但出于好奇,这段代码的来源是 on github here, line 419
问题的第一部分很简单:std::get<I>(t).doSomething()
可能returnvoid
。因此,您不能直接将此表达式用作 expand_calls_hack()
的参数。即使 doSomething()
returns 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
完全一样。
我在一个开源项目中找到的代码基本上是这样的:
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)...);
}
};
我认为这个 "construct" 用于为每个元组元素调用 doSomething()
。然而,在我看来,对 doSomething()
的调用顺序是未定义的,至少在 C++03 中是这样的。这将是一个错误,因为调用有副作用。我有两个问题:
(tupleElement.doSomething(), 0) 有什么用 - 那是逗号运算符吗?我认为这与使用 expand_calls_hack 扩展调用有关。
如何解决这个问题,以便从左到右计算调用?请注意,我需要在 VC2013 上进行编译。我试过扩展 lambda 列表并按顺序调用它,但我无法编译它。
我希望我没有遗漏太多上下文,但出于好奇,这段代码的来源是 on github here, line 419
问题的第一部分很简单:std::get<I>(t).doSomething()
可能returnvoid
。因此,您不能直接将此表达式用作 expand_calls_hack()
的参数。即使 doSomething()
returns 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
完全一样。