#include <utility>
template<std::size_t ...I>
void print_sequence(std::index_sequence<I...>)
{
int arr[] = {(std::cout << I << " ",0) ...};
std::cout <<std::endl;
}
void func()
{
using seq = std::make_index_sequence<5>;
print_sequence(seq{});
}
std::make_index_sequence 的核心用途:在编译期生成整数序列,然后在运行时或编译期利用这些序列来展开参数包、遍历容器或处理编译期已知大小的数据结构。它是 C++ 模板元编程中非常实用的工具。
这是一个非常经典的 C++17 折叠表达式(fold expression)的例子。让我详细解释这段代码的每一部分:
template<std::size_t... I> // 1. 可变参数模板,接受多个 size_t 值 void print_sequence(std::index_sequence<I...>) { // 2. 参数是一个 index_sequence ((std::cout << I << " "), ...); // 3. 逗号折叠表达式,逐个打印 std::cout << std::endl; // 4. 换行 }
std::size_t... II 是一个模板参数包,可以包含多个 std::size_t 类型的值
例如:I 可能是 0, 1, 2, 3, 4
std::index_sequence<I...>接受一个 std::index_sequence 类型的参数
这个参数包含了模板参数包 I... 中的值
((std::cout << I << " "), ...)这是 C++17 的逗号折叠表达式,展开过程如下:
假设传入的 index_sequence 是 <0, 1, 2, 3>:
// 编译时展开为: ((std::cout << 0 << " "), (std::cout << 1 << " "), (std::cout << 2 << " "), (std::cout << 3 << " "));
#include <iostream> #include <utility> template<std::size_t... I> void print_sequence(std::index_sequence<I...>) { ((std::cout << I << " "), ...); std::cout << std::endl; } int main() { // 直接传入 index_sequence print_sequence(std::index_sequence<0, 1, 2, 3, 4>{}); // 输出: 0 1 2 3 4 // 使用 make_index_sequence 生成 print_sequence(std::make_index_sequence<5>{}); // 输出: 0 1 2 3 4 // 空序列 print_sequence(std::index_sequence<>{}); // 输出: (空行) // 不连续的序列 print_sequence(std::index_sequence<0, 2, 4, 6>{}); // 输出: 0 2 4 6 return 0; }
template<std::size_t... I> void print_sequence_with_comma(std::index_sequence<I...>) { // 第一个元素不加逗号,后面的元素前加逗号 std::size_t dummy[] = {((std::cout << (I == 0 ? "" : ", ") << I), 0)...}; std::cout << std::endl; } // 或者使用折叠表达式(C++17) template<std::size_t... I> void print_sequence_fancy(std::index_sequence<I...>) { std::cout << "["; ((std::cout << (I == 0 ? "" : ", ") << I), ...); std::cout << "]" << std::endl; }
#include <string> #include <sstream> template<std::size_t... I> std::string sequence_to_string(std::index_sequence<I...>) { std::ostringstream oss; ((oss << I << " "), ...); return oss.str(); }
template<typename... Args> void print_with_indices(Args&&... args) { print_with_indices_impl(std::index_sequence_for<Args...>{}, std::forward<Args>(args)...); } template<std::size_t... I, typename... Args> void print_with_indices_impl(std::index_sequence<I...>, Args&&... args) { // 同时打印索引和对应的值 ((std::cout << "[" << I << "]: " << args << " "), ...); } // 使用 // print_with_indices(10, 3.14, "hello"); // 输出: // [0]: 10 // [1]: 3.14 // [2]: hello
// 一元右折叠 template<std::size_t... I> void print_unary_right(std::index_sequence<I...>) { (std::cout << ... << I); // 输出: 0123(没有空格) std::cout << std::endl; } // 二元折叠 template<std::size_t... I> void print_binary(std::index_sequence<I...>) { (std::cout << ... << " "); // 错误:二元折叠需要初始化值 // 正确写法: ((std::cout << I << " "), ...); // 逗号运算符 }
C++17 要求:折叠表达式是 C++17 的特性
逗号运算符:这里使用逗号运算符来按顺序执行多个表达式
参数包展开:... 必须在括号内,不能直接放在语句末尾
空包处理:如果参数包为空,折叠表达式可能不执行任何操作
这个模式在 C++ 模板元编程中非常常见,特别是在需要按顺序处理编译期已知的整数序列时。
C++14 不支持折叠表达式(fold expressions)。折叠表达式是 C++17 引入的特性 -2。
你可能遇到过类似这样的代码在 C++14 模式下也能编译:
template<typename... Args> auto sum(Args... args) { return (args + ...); // 折叠表达式 }
这是因为 GCC 和 Clang 等编译器默认将 C++17 的部分特性作为扩展提供,即使你在 C++14 模式下编译 。编译器这样做是因为:
折叠表达式是纯新增语法,不会破坏已有的 C++14 代码
提供这些扩展可以提高编译器的实用性
但是,你会收到警告:
warning: pack fold expression is a C++17 extension [-Wc++17-extensions]
如果你想确保代码严格符合 C++14 标准,可以使用以下编译器选项:
# GCC/Clang: 将扩展视为错误 g++ -std=c++14 -pedantic-errors your_file.cpp # 或者只显示警告 g++ -std=c++14 -pedantic your_file.cpp
使用 -pedantic-errors 后,折叠表达式会导致编译错误 -5。
如果你需要在 C++14 中实现类似折叠表达式的功能,有几种常用方法:
// 基础情况:单个参数 template<typename T> auto fold_sum(T&& t) { return std::forward<T>(t); } // 递归情况:多个参数 template<typename T, typename... Args> auto fold_sum(T&& t, Args&&... args) { return std::forward<T>(t) + fold_sum(std::forward<Args>(args)...); } // 使用示例 auto result = fold_sum(1, 2, 3, 4, 5); // 15
template<typename... Args> auto sum_all(Args... args) { using common_type = std::common_type_t<Args...>; common_type result{}; // 使用逗号运算符和初始化列表展开参数包 int dummy[] = {0, (result += args, 0)...}; (void)dummy; // 避免未使用变量警告 return result; } // 平方和示例 template<typename... Args> auto sum_of_squares(Args... args) { return sum_all((args * args)...); }
template<typename... Args> auto sum_with_index(Args... args) { using common_type = std::common_type_t<Args...>; std::array<common_type, sizeof...(Args)> arr = {args...}; common_type result{}; for (const auto& val : arr) { result += val; } return result; }
有一些库(如 cpp-fold)专门为 C++14 提供了类似折叠表达式的功能 :
#include <cppfold/fold.h> int main() { using namespace cppfold; // 左折叠 int sum = lfold<plus>(1, 2, 3, 4); // 10 // 右折叠 int product = rfold<multiplies>(2, 3, 4); // 24 return 0; }