You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何嵌套std::bind仍提前计算时间?《Effective Modern C++》实践疑问

问题描述

我读了《Effective Modern C++》的Item34:优先使用lambda而非std::bind,里面提到嵌套std::bind可以延迟表达式求值,直到目标函数被调用。于是写了测试代码验证,但两次调用绑定后的setSoundB函数时,输出的时间值完全一样,看起来表达式在调用std::bind时就计算了,这是为什么?

测试代码

#include <bits/stdc++.h>
#include <boost/type_index.hpp>
#include <memory>

using namespace std;

using Time = std::chrono::steady_clock::time_point;

enum class Sound
{
    Beep,
    Siren,
    Whistle
};

using Duration = std::chrono::steady_clock::duration;

template <typename T>
std::underlying_type_t<T> printEnum(T const value)
{
    return static_cast<std::underlying_type_t<T>>(value);
}

void setAlarm(Time t, Sound s, Duration d)
{
    cout << "time:" << t.time_since_epoch().count() << "  ring"
         << "  duraion:" << (std::chrono::duration_cast<std::chrono::seconds>(d)).count()
         << "  sound:" << static_cast<typename std::underlying_type<Sound>::type>(s)
         << "  sound:" << printEnum(s)
         << endl;
}

int main()
{
    using namespace std::chrono; 

    using namespace std::literals;

    using namespace std::placeholders; 
    auto setSoundB = std::bind(setAlarm,
                               std::bind(std::plus<>(), steady_clock::now(), 1h), _1, seconds(30));

    cout << "function calc time:" << steady_clock::now().time_since_epoch().count() << endl;
    setSoundB(Sound::Beep);
    cout << "function calc time:" << steady_clock::now().time_since_epoch().count() << endl;
    setSoundB(Sound::Beep);
    return 0;
}

运行结果

function calc time:1670995052139460000
time:1670998652139145000  ring  duraion:30  sound:0  sound:0
function calc time:1670995052139480000
time:1670998652139145000  ring  duraion:30  sound:0  sound:0

原因分析与解决方法

核心问题出在你传递给内层std::bind的steady_clock::now()——这个函数调用在创建bind对象时就被执行了,而不是延迟到setSoundB调用时。

std::bind的参数处理逻辑是:当你传递一个表达式(比如函数调用)作为绑定参数时,除非这个参数是std::placeholders占位符,或者是另一个bind返回的函数对象,否则它会立即求值,把结果存储到bind对象里。

你这里的steady_clock::now()是一个立即执行的函数调用,它的返回值(当前时间点)会被直接计算出来,然后和1h一起传给std::plus<>的bind对象。内层bind对象存储的是这个计算后的时间值,而不是“调用steady_clock::now()再加1小时”的逻辑。

修复方案

要实现延迟求值,你需要把steady_clock::now()包装成一个可调用对象,让它在每次setSoundB调用时才执行。比如用lambda代替内层的bind:

auto setSoundB = std::bind(setAlarm,
                           []{ return steady_clock::now() + 1h; }, 
                           _1, 
                           seconds(30));

或者更符合《Effective Modern C++》Item34建议的,直接用lambda实现整个逻辑,可读性和灵活性都更好:

auto setSoundB = [](Sound s) {
    setAlarm(steady_clock::now() + 1h, s, 30s);
};

这样每次调用setSoundB时,steady_clock::now()才会被执行,得到的时间值自然每次都不一样。


内容的提问来源于stack exchange,提问作者OverDrive

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 00:10:22