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已成为历史遗产,应逐渐被替代。

相关推荐
fie88898 小时前
基于MATLAB的转子动力学建模与仿真实现(含碰摩、不平衡激励)
开发语言·算法·matlab
lly2024068 小时前
C# 变量作用域
开发语言
时艰.8 小时前
java性能调优 — 高并发缓存一致性
java·开发语言·缓存
MSTcheng.8 小时前
【C++】C++智能指针
开发语言·c++·智能指针
无小道8 小时前
Qt——网络编程
开发语言·qt
wazmlp0018873698 小时前
第五次python作业
服务器·开发语言·python
云深处@8 小时前
【C++11】部分特性
开发语言·c++
尘缘浮梦8 小时前
websockets简单例子1
开发语言·python
jxy99988 小时前
mac mini 安装java JDK 17
java·开发语言·macos
独望漫天星辰8 小时前
C++ 树结构进阶:从工程化实现到 STL 底层与性能优化
开发语言·c++