变参模板4:折叠表达式

题目描述

折叠表达式允许在参数包中连续“折叠”地做二元运算,并得到一个最终的结果。比如对于形参包 1, 2, 3, 4,在其上左折叠地做“加法”,效果就如同 (((1+2)+3)+4)。类似地还有右折叠,只不过是先对最后两个形参做运算。

左折叠的折叠表达式写成 (... op args),其中 op 是二元运算符,args 是形参包,如 (... + args) 就是左折叠地做加法。右折叠就是将省略号放在运算符右侧:(args op ...)。注意折叠表达式的外围括号是必需的。

折叠表达式既可以作用在函数形参包上,也可以作用在模板形参包上。比如对于 template <int... Ns> /* ... */ 这种模板,出现 (... * Ns) 的场合就会被替换为所有 Ns 非类型实参的乘积。

下面请实现函数模板 sum 返回所有实参的和、函数模板 sumOfSquare 返回所有实参的平方和、类模板 Sum 具有静态常量成员 value 以计算模板实参的和。

注:很显然这种折叠要求形参包的形参个数必须大于等于 2。对于数量更少的形参包,可以用带初始值的折叠表达式,写成 (init op ... op args)(args op ... op init)。这道题目对这种形式不做考察,所以这里只是简单介绍一下。

关于输入

关于输出

见样例输出

参考答案

#include <iostream>
#include <string>

template <typename... Ts>
auto sum(Ts... ts) {
    return (... + ts);
}

template <typename... Ts>
auto sumOfSquare(Ts... ts) {
    return (... + (ts * ts));
}

template <int... Ns>
struct Sum {
    static constexpr int value = (... + Ns);
};

int main() {
    std::cout << sum(6, 6, 6) << std::endl;
    std::cout << Sum<1, 2, 3, 4, 5>::value << std::endl;
    std::cout << sum(1, 4, 2, 8, 5, 7) << std::endl;
    std::cout << Sum<4, 3, 9, 9>::value << std::endl;
    std::cout << sum(std::string{}, "Hello", ", ", "world", '!') << std::endl;
    std::cout << sumOfSquare(3, 4, 12, 13) << std::endl;
}