焦小花同学

perfect_forwarding:完美转发工具,通用代码的设计范本

Image

等你来关注!

perfect_forwarding:完美转发工具,通用代码的设计范本

C++ 有个让人又爱又恨的特性——模板。虽然很强,但写起来总觉得绕来绕去,尤其是参数传递时,有时候需要左值引用,有时候需要右值引用,手一抖还会搞出一堆拷贝和移动操作。为了解决这种头疼的问题,完美转发(perfect forwarding)就站了出来。它是写通用代码的好帮手,尤其是在构建库的时候,能让你的代码更高效、更优雅。

完美转发到底是个啥?

Image

简单来说,完美转发就是让你的函数参数“原封不动”地传递给另一个函数,既能传左值,也能传右值,还能保证效率。就好比你有一台超市的传送带,不管搬上去的是脆弱的鸡蛋还是坚固的钢铁,传过去的时候,它们的状态都能完好无损。

实现完美转发的关键在于两个东西: 模板类型参数(T&&) 和std::forward。T&& 是万能引用,能同时接受左值和右值,而 std::forward 则是个“搬运工”,帮你把东西正确地搬过去。

用完美转发写一个“万能工厂”

Image

假设我们要写一个工厂函数,用来创建任意类型的对象。用完美转发,这个工厂函数不仅可以接受各种参数,还能高效地做到不拷贝、不多余构造。

#include <iostream>

#include <utility>

template <typename T, typename... Args>

T create(Args&&... args) {

return T(std::forward<Args>(args)...);

}

class Widget {

public:

Widget(int a, double b) {

std::cout << “Widget constructor called:” << a << “, ” << b << “\n”;

}

};

int main() {

auto w = create<Widget>(42, 3.14);

return 0;

}

代码解析

  1. Args&&... args:这是万能引用,可以接受任意数量、任意类型的参数。

  2. std::forward<Args>(args)...:这个搬运工根据参数的原始类型(左值 or 右值)正确转发,避免不必要的拷贝或移动。

  3. 实际应用 :上面的代码中,create 函数精准地把 42 和 3.14 传递给了 Widget 的构造函数。

温馨提示:如果你忘了用 std::forward,参数会被强制转成左值引用,结果可能是性能问题,甚至直接编译不过。

常见问题:什么时候用 std::move,什么时候用 std::forward?

这个问题经常让人迷糊。简单总结一下:

  • std::move 是“强制搬家”。无论参数原来是啥,它都把参数变成右值,意思是“你别用了,归我了”。

  • std::forward 是“原样转发”。它会根据参数原来的类型,决定是左值还是右值。

举个例子:

#include <iostream>

#include <utility>

void take(int&& x) {

std::cout << “Rvalue:” << x << “\n”;

}

void take(const int& x) {

std::cout << “Lvalue:” << x << “\n”;

}

template <typename T>

void forward_example(T&& val) {

take(std::forward<T>(val));

}

int main() {

int a = 10;

forward_example(a);// 输出 Lvalue:10

forward_example(20);// 输出 Rvalue:20

return 0;

}

温馨提示:std::move 是个强制任性的小孩,容易“滥用”。一般情况下,优先考虑 std::forward,尤其是在模板代码里。

用完美转发实现通用包装器

Image

有时候,你可能想写一个函数来包装另一个函数,比如用来加点日志、统计时间啥的。完美转发是实现这种通用工具的神器。

#include <iostream>

#include <utility>

template <typename Func, typename... Args>

auto wrapper(Func&& func, Args&&... args) {

std::cout << “Before calling function\n”;

auto result = std::forward<Func>(func)(std::forward<Args>(args)...);

std::cout << “After calling function\n”;

return result;

}

void say_hello(const std::string& name) {

std::cout << “Hello, ” << name << “\n”;

}

int main() {

wrapper(say_hello, “C++”);

return 0;

}

这个代码的亮点在于,wrapper 函数能完美转发参数给 func,而不会强制改变参数的类型或状态。

小结

Image

完美转发不难,关键是理解它的核心思想: 精准、高效地转发参数 。记住两个工具——T&& 和 std::forward,多写点代码练练手,你会发现它其实是个很实用的小工具。

E

N

D

Image

Image

分享

收藏

在看

点赞