在不使用折叠表达式的情况下以正确顺序处理模板参数包的值的最简单方法是什么 (C++11)
What is the simplest way to process the values of a template parameter pack in the correct order without using fold-expressions (C++11)
我想将以下代码反向移植到 C++11:
template<unsigned i>
static void bar() { /* some code with compile-time optimizations for each value i */ }
template <unsigned... I>
void f()
{
((bar<I>()),...);
}
参数包 I 的每个值的 'bar' 调用顺序很重要 - 我相信它在上面的 C++17 实现中起作用,因为折叠表达式使用逗号运算符。
我最初选择了显式递归实现:
template <unsigned... I>
typename std::enable_if<sizeof...(I) == 0>::type g() {}
template <unsigned head, unsigned... I>
void g()
{
bar<head>();
g<I...>();
}
这似乎可行,但需要 g() 的两个实现。在尝试使用单个函数时,我读到包扩展将在 make_tuple 中发生,并认为这会起作用:
template<unsigned... I>
static void h1()
{
std::make_tuple( (bar<I>(), 0)... );
}
不幸的是,这不会对执行顺序提供任何保证——事实上,对于 gcc,执行顺序正好相反。
或者,我可以使用 braced-init-list:
template<unsigned... I>
static void h2()
{
using expand = int[];
expand{ 0, ( bar<I>(), 0) ... };
}
这似乎保留了 gcc 的顺序,但我无法确定这是否只是巧合。
所以,具体问题是:
- h2的执行能保证正确的执行顺序吗?
- 是否有我错过的替代实现?
您的 h1
不能保证任何版本的 C++ 中的计算顺序是正确的,因为函数参数的初始化顺序不确定。您的 h2
确实保证了 C++11 及更高版本中的评估顺序。事实上,任何将调用作为 {
支撑列表 }
的元素的方法都将保证它。
自 C++11 以来的所有 C++ 版本都包含段落 [dcl.init.list]/4:
Within the initializer-list of a braced-init-list, the initializer-clauses, including any that result from pack expansions ([temp.variadic]), are evaluated in the order in which they appear. That is, every value computation and side effect associated with a given initializer-clause is sequenced before every value computation and side effect associated with any initializer-clause that follows it in the comma-separated list of the initializer-list. [Note: This evaluation ordering holds regardless of the semantics of the initialization; for example, it applies when the elements of the initializer-list are interpreted as arguments of a constructor call, even though ordinarily there are no sequencing constraints on the arguments of a call. – end note]
我想将以下代码反向移植到 C++11:
template<unsigned i>
static void bar() { /* some code with compile-time optimizations for each value i */ }
template <unsigned... I>
void f()
{
((bar<I>()),...);
}
参数包 I 的每个值的 'bar' 调用顺序很重要 - 我相信它在上面的 C++17 实现中起作用,因为折叠表达式使用逗号运算符。
我最初选择了显式递归实现:
template <unsigned... I>
typename std::enable_if<sizeof...(I) == 0>::type g() {}
template <unsigned head, unsigned... I>
void g()
{
bar<head>();
g<I...>();
}
这似乎可行,但需要 g() 的两个实现。在尝试使用单个函数时,我读到包扩展将在 make_tuple 中发生,并认为这会起作用:
template<unsigned... I>
static void h1()
{
std::make_tuple( (bar<I>(), 0)... );
}
不幸的是,这不会对执行顺序提供任何保证——事实上,对于 gcc,执行顺序正好相反。 或者,我可以使用 braced-init-list:
template<unsigned... I>
static void h2()
{
using expand = int[];
expand{ 0, ( bar<I>(), 0) ... };
}
这似乎保留了 gcc 的顺序,但我无法确定这是否只是巧合。
所以,具体问题是:
- h2的执行能保证正确的执行顺序吗?
- 是否有我错过的替代实现?
您的 h1
不能保证任何版本的 C++ 中的计算顺序是正确的,因为函数参数的初始化顺序不确定。您的 h2
确实保证了 C++11 及更高版本中的评估顺序。事实上,任何将调用作为 {
支撑列表 }
的元素的方法都将保证它。
自 C++11 以来的所有 C++ 版本都包含段落 [dcl.init.list]/4:
Within the initializer-list of a braced-init-list, the initializer-clauses, including any that result from pack expansions ([temp.variadic]), are evaluated in the order in which they appear. That is, every value computation and side effect associated with a given initializer-clause is sequenced before every value computation and side effect associated with any initializer-clause that follows it in the comma-separated list of the initializer-list. [Note: This evaluation ordering holds regardless of the semantics of the initialization; for example, it applies when the elements of the initializer-list are interpreted as arguments of a constructor call, even though ordinarily there are no sequencing constraints on the arguments of a call. – end note]