作用:将复杂的未知表达式折叠为已知的简单表达式

引用折叠规则遵循以下几个原则:

  1. 如果两个模板参数都是左值引用(Lvalue references),结果是一个左值引用。
  2. 如果一个模板参数是左值引用,而另一个是右值引用(Rvalue reference),结果是一个右值引用。
  3. 如果两个模板参数都是右值引用,即使它们的类型不同,结果仍然是一个右值引用。

#include <iostream>

template<typename T>
void foo(T&& val) {
    std::cout << "foo(T&&): 传入的参数是右值引用" << std::endl;
}

template<typename T>
void foo(T& val) {
    std::cout << "foo(T&): 传入的参数是左值引用" << std::endl;
}

int main() {
    int x = 42;
    const int& y = x;

    foo(x);   // 调用 foo(T&)
    foo(y);   // 调用 foo(T&)
    foo(123); // 调用 foo(T&&)

    return 0;
}

在上面的示例中,我们定义了两个函数模板 foo,分别接受一个左值引用和一个右值引用作为参数。在 main 函数中,我们分别传递了一个左值 x、一个 const 左值引用 y 和一个字面量右值 123 给 foo 函数。

根据引用折叠规则,foo(x) 和 foo(y) 都会调用接受左值引用的函数模板 foo(T&),而 foo(123) 则会调用接受右值引用的函数模板 foo(T&&)。

运行以上代码,输出将是:

这个示例演示了引用折叠的基本原理和使用场景。它使编写通用的函数模板能够更好地处理各种类型的参数,无论是左值还是右值。

#include <iostream>

template<typename... Args>
void print(Args&&... args) {
    (std::cout << ... << args) << std::endl;
}

int main() {
    int x = 42;
    double y = 3.14;
    std::string str = "Hello, World!";

    print("x = ", x, ", y = ", y, ", str = ", str);

    return 0;
}

在上述代码中,我们定义了一个名为 print 的可变参数模板函数。该函数接受任意数量的参数,并将它们打印到标准输出。

在函数体内部的 (std::cout << ... << args) 使用了 C++17 中的折叠表达式(Fold Expression)。这个折叠表达式使用了逗号操作符和左移操作符,逐个将参数 args 插入到 std::cout 流中。

通过使用引用折叠,即 Args&&... args,我们可以接受传递给 print 函数的任意类型的参数,并将它们简化为已知的简单表达式,并且该函数可以正确地处理各种类型的参数。

在 main 函数中,我们传递了一个整型变量 x、一个双精度浮点数变量 y,以及一个字符串 str 给 print 函数。然后,print 函数将这些参数打印到标准输出。

运行以上代码,输出将是:

关于完美转发可以见: