itz*_*ode 1 c++ for-loop template-meta-programming
最近我发现了这个关于使用模板展开循环的StackOverflow 答案。答案指出“这个想法适用于 C++11”,我最终得到了这个:
namespace tmpl {
namespace details {
template<class T, T... values>
class integer_sequence {
public:
static constexpr size_t size() { return sizeof...(values); }
};
template<class T, class N, class... Is>
struct make_integer_sequence_helper :
make_integer_sequence_helper<T, std::integral_constant<T, N::value - 1>, std::integral_constant<T, N::value - 1>, Is...> {};
template<class T, class... Is>
struct make_integer_sequence_helper<T, std::integral_constant<T, 0>, Is...> {
using type = integer_sequence<T, Is::value...>;
};
template<class T, T N>
using make_integer_sequence = typename make_integer_sequence_helper<T, std::integral_constant<T, N>>::type;
template<class... Ts>
void variadic_noop(Ts... params) {}
template<class F, class T>
int call_and_return_0(F&& f, T i) {f(i); return 0;}
template<class T, T... Is, class F>
void loop(integer_sequence<T, Is...>, F&& f) {
variadic_noop(call_and_return_0(f, Is)...);
}
}
template<class T, T max, class F>
void loop(F&& f) {
details::loop(details::make_integer_sequence<T, max>{}, f);
}
}
Run Code Online (Sandbox Code Playgroud)
让我们举一个简单的例子来说明如何使用该模板:
tmpl::loop<size_t, 20>([&](size_t idx) {
cout << "Loop " << idx << std::endl;
});
Run Code Online (Sandbox Code Playgroud)
当我使用其他答案中的 C++17 代码时,它从 0 迭代到 19。但是,我编写的 C++11 jank 从 19 迭代到 0。
理论上,当details::loop()展开时它应该变成这样:
variadic_noop(call_and_return_0(f, 0), call_and_return_0(f, 1), call_and_return_0(f, 2), ...);
Run Code Online (Sandbox Code Playgroud)
call_and_return_0(f, 19)那么,如果它是最后一个参数,为什么 C++ 首先运行呢variadic_noop()?
函数调用中函数参数的求值可以是从左到右、从右到左或任何其他顺序,这不需要是可预测的。
但是,当您有一个子表达式 , , ..., 由逗号运算符分隔的单个表达式e_1, e_2, ..., e_n时e_1,e_2子表达式e_n将始终按从左到右的顺序计算,因为逗号运算符保证其左操作数在其右操作数之前求值。(在 C++17 之前,此保证仅适用于内置逗号运算符,不适用于任何重载运算符。)
尽管函数调用也使用逗号,但函数调用中的逗号不强制从左到右求值。链接答案中的代码使用 C++17 折叠表达式创建一系列由逗号运算符分隔的表达式。