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

相关推荐
资生算法程序员_畅想家_剑魔3 分钟前
Java常见技术分享-12-多线程安全-锁机制
java·开发语言
胡楚昊18 分钟前
NSSCTF动调题包通关
开发语言·javascript·算法
2401_8370885025 分钟前
简要总结 HashSet 和 HashMap(Java)
java·开发语言
毕设源码-钟学长1 小时前
【开题答辩全过程】以 基于Java的家政服务管理系统的设计与实现为例,包含答辩的问题和答案
java·开发语言
小白学大数据1 小时前
Java 爬虫对百科词条分类信息的抓取与处理
java·开发语言·爬虫
zmzb01031 小时前
C++课后习题训练记录Day56
开发语言·c++
编程小Y1 小时前
C++ Insights
开发语言·c++
小c君tt1 小时前
QT中想在QTextEdit控件中使用Qslog日志输出出现问题原因及解决方法
开发语言·qt
王老师青少年编程2 小时前
csp信奥赛C++标准模板库STL案例应用5
c++·stl·set·集合·标准模板库·csp·信奥赛
历程里程碑2 小时前
hot 206
java·开发语言·数据结构·c++·python·算法·排序算法