遇到如下代码,对于 func2 没有看懂。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
template<typename T>
struct DataStruct {
    DataStruct(int i, T n): index(i), value(n) {}

    int index;
    T value;
};

template<typename T>
auto func1(string prefix, int i, T n) {
    cout << __FUNCTION__ << "(" << prefix << ", " << i << ", " << n << ")" << endl;
}

template<typename... T> // mark 1
auto func2(string p, const DataStruct<T>&&... s) { // mark 2
    (func1(p, s.index, s.value), ...);  // mark 3
}


int main() {

    func2<string>("hi ", DataStruct<string>(12, "Tom"), DataStruct<string>(29, "Mason"), DataStruct<int>(100, 999));
    cout << "done\n";
}

// execute result
/*
func1(hi , 12, Tom)
func1(hi , 29, Mason)
func1(hi , 100, 999)
done
*/

查询后得知是 C++ 的可变模板(variadic template)和折叠表达式(fold expression)。

可变模板(Variadic Template)

可变模板是 C++11 引入的功能,允许模板接受不定数量的模板参数。这对于需要处理多个参数类型或数量的情况非常有用。例如:

1
2
3
4
5
6
7
void coutX() {} // 终止条件

template<typename T, typename... Types>
void coutX(const T& t, const Types&... args) {
    cout << t << endl; // cout 1st argument
    coutX(args...); // 对可变参数递归调用 coutX
}

其中非模板重载函数 contX() 是递归调用的终止条件。

折叠表达式(Fold Expression)

折叠表达式是 C++17 为配合 Variadic Template 使用引入的功能,它允许在模板展开过程中进行一元或二元操作。折叠表达式有两种形式:左折叠和右折叠。

对应的展开关系如下:

Fold Expression Evaluation
(E op ...) (E1 op (... op (EN-1 op EN)))
(... op E) (((E1 op E2) op ...) op EN)
(E op ... op I) (E1 op (... op (EN−1 op (EN op I))))
(I op ... op E) ((((I op E1) op E2) op ...) op EN)

其中

  • 左折叠形式:(expr op ...),其中 expr 是一个表达式,op 是二元操作符。
  • 右折叠形式:(... op expr),同样,expr 是一个表达式,op 是二元操作符。
1
2
3
4
template<typename... T> // variadic template
auto func2(string p, const DataStruct<T>&&... s) { // variadic template
    (func1(p, s.index, s.value), ...); // fold expression
}

在上面的例子中,func2 函数的实现使用了右折叠表达式(expr op ...)。其对应关系如下:

  • expr : func1(p, s.index, s.value)
  • op : ,
  • ... : ...

另一个 (I op ... op E) 折叠表达式的例子:

1
2
3
4
5
template<typename... Values>
auto sum(Values const&... values) {
    return (0 + ... + values);
}
sum(1, 2, 3, 4); // same as (0 + 1 + 2 + 3 + 4)

其中

  • I : 0
  • op : +
  • ... : ...
  • op : +
  • E : values

对于前面 contX 的例子,在使用 C++17Fold Expression 后就不需要再手动实现递归和终止条件,可简化如下:

1
2
3
void coutX(const Types&... args) {
    cout << ... << args << endl;
}