make_index_sequence_t

 Tue, 21-Apr-2026 14:19:13

#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++ 模板元编程中非常实用的工具。

template<std::size_t... I> void print_sequence(std::index_sequence<I...>) { ((std::cout << I << " "), ...); std::cout << std::endl; }

这是一个非常经典的 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. 换行
}

工作原理

1. 模板参数 std::size_t... I

  • I 是一个模板参数包,可以包含多个 std::size_t 类型的值

  • 例如:I 可能是 0, 1, 2, 3, 4

2. 函数参数 std::index_sequence<I...>

  • 接受一个 std::index_sequence 类型的参数

  • 这个参数包含了模板参数包 I... 中的值

3. 折叠表达式 ((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;
}

更复杂的变体

1. 添加分隔符

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;
}

2. 返回字符串而不是打印

#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();
}

3. 同时打印索引和值

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 << " "), ...);  // 逗号运算符
}

注意事项

  1. C++17 要求:折叠表达式是 C++17 的特性

  2. 逗号运算符:这里使用逗号运算符来按顺序执行多个表达式

  3. 参数包展开... 必须在括号内,不能直接放在语句末尾

  4. 空包处理:如果参数包为空,折叠表达式可能不执行任何操作

这个模式在 C++ 模板元编程中非常常见,特别是在需要按顺序处理编译期已知的整数序列时。

 

C++14 不支持折叠表达式(fold expressions)。折叠表达式是 C++17 引入的特性 -2

为什么你的代码能在 C++14 模式下编译?

你可能遇到过类似这样的代码在 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 中的替代方案

如果你需要在 C++14 中实现类似折叠表达式的功能,有几种常用方法:

1. 递归模板函数

// 基础情况:单个参数
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

2. 初始化列表展开(数组技巧)

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)...);
}

3. 使用 std::index_sequence 配合数组

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;
}

4. 第三方库支持

有一些库(如 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;
}