Effective Modern C++ 条款34:优先考虑lambda而非std::bind

Effective Modern C++ 条款34:优先考虑lambda而非std::bind

在C++11及更高版本中,lambda表达式几乎总是比std::bind更好的选择。本文将详细探讨为何lambda优于std::bind,并通过具体示例展示两者的差异。

std::bind的历史背景

C++11中的std::bind是C++98的std::bind1st和std::bind2nd的后续,但在2005年已经非正式成为了标准库的一部分。那时标准化委员采用了TR1的文档,其中包含了bind的规范(在TR1中,bind位于std::tr1::bind命名空间)。

可读性对比

优先选择lambda而非std::bind的最重要原因是lambda更易读。考虑以下设置警报器的函数:

cpp 复制代码
using Time = std::chrono::steady_clock::time_point;
enum class Sound { Beep, Siren, Whistle };
using Duration = std::chrono::steady_clock::duration;

void setAlarm(Time t, Sound s, Duration d);

使用lambda实现一小时后响30秒的警报器:

cpp 复制代码
auto setSoundL = [](Sound s) {
    using namespace std::chrono;
    setAlarm(steady_clock::now() + hours(1), s, seconds(30));
};

而使用std::bind的等效实现:

cpp 复制代码
using namespace std::chrono;
using namespace std::placeholders;

auto setSoundB = std::bind(setAlarm,
                          steady_clock::now() + 1h,  // 这里有错误
                          _1,
                          30s);

第一个问题在于std::bind会在绑定时计算时间,而非调用时。正确的实现需要嵌套bind调用:

cpp 复制代码
auto setSoundB = std::bind(setAlarm,
                          std::bind(std::plus<>(), 
                                   std::bind(steady_clock::now), 
                                   1h),
                          _1,
                          30s);

显然,lambda版本更简洁明了。

性能考虑

lambda通常能生成更高效的代码。在lambda中,函数调用可以被编译器内联,而std::bind通过函数指针调用,通常无法内联。

重载处理

当函数存在重载时,std::bind会遇到问题:

cpp 复制代码
enum class Volume { Normal, Loud, LoudPlusPlus };
void setAlarm(Time t, Sound s, Duration d, Volume v);  // 重载版本

auto setSoundB = std::bind(setAlarm,  // 错误!哪个setAlarm?
                          std::bind(std::plus<>(),
                                   steady_clock::now(),
                                   1h),
                          _1,
                          30s);

必须使用强制转换解决:

cpp 复制代码
using SetAlarm3ParamType = void(*)(Time t, Sound s, Duration d);
auto setSoundB = std::bind(static_cast<SetAlarm3ParamType>(setAlarm),
                          std::bind(std::plus<>(),
                                   steady_clock::now(),
                                   1h),
                          _1,
                          30s);

而lambda无需任何修改就能正常工作【1†source】。

捕获语义

std::bind总是按值拷贝实参,要按引用传递需使用std::ref【5†source】。而lambda可以明确指定值捕获或引用捕获:

cpp 复制代码
Widget w;
auto compressRateL = [w](CompLevel lev) { return compress(w, lev); };  // 值捕获明确
auto compressRateB = std::bind(compress, std::ref(w), _1);  // 引用捕获不明显

C++11中std::bind的合理用例

在C++11中,std::bind仍有以下两个合理用例【1†source】:

  1. 移动捕获:C++11的lambda不支持移动捕获,可以通过std::bind模拟
  2. 多态函数对象:绑定带有模板化函数调用运算符的对象
cpp 复制代码
class PolyWidget {
public:
    template<typename T>
    void operator()(const T& param);
};

PolyWidget pw;
auto boundPW = std::bind(pw, _1);  // 接受任意类型参数

但在C++14中,这两个用例都可以用lambda更好实现:

cpp 复制代码
auto boundPW = [pw](const auto& param) { pw(param); };  // C++14多态lambda

结论

与std::bind相比,lambda具有以下优势:

  1. 更易读,更具表达力
  2. 通常更高效
  3. 更直观的参数传递和捕获语义
  4. 更好的重载处理能力

在C++14及更高版本中,几乎没有使用std::bind的合理场景。对于仍在使用C++11的项目,仅在需要移动捕获或绑定多态函数对象时考虑std::bind。

记住:在大多数情况下,lambda应该是你的首选工具,std::bind已成为历史遗产,应逐渐被替代。

相关推荐
D_evil__1 小时前
[C++高频精进] 网络编程:网路基础
c++
无敌最俊朗@1 小时前
C++ 内存管理与编译原理 (面试复习2)
java·开发语言·jvm
mjhcsp1 小时前
P1220关路灯mjhcsp
c++·动态规划
火山灿火山1 小时前
Qt信号和槽
开发语言·qt
这样の我1 小时前
java 模拟chrome tls指纹
java·开发语言·chrome
m0_661279181 小时前
学习笔记-安装并启动 Jupyter Noteboo
开发语言·python
代码or搬砖1 小时前
常见的五个编译时异常和常见的五个编译时异常
开发语言·php
kyle~1 小时前
算法与数据结构---并查集(Union-Find)
数据结构·c++·算法
烽火聊员1 小时前
SSLSocket 服务器端WPF C#测试代码
开发语言·c#·wpf·ssl