称std::bind
返回的函数对象为绑定对象。考虑在程序的某处我们想要设置在一小时后发出警报并持续30秒,考虑如下代码:
using Time = std::chrono::steady_clock::time_point;
enum class Sound {Beep, Siren, Whistle};
using Duration = std::chrono::steady_clock::duration;
// 在时刻t,发出声音s,持续时常d
void setAlarm(Time t, Sound s, Duration d);
// 接受一个指定声音,该声音将在1小时后发出,并持续30秒
auto setSoundL =
[] (Sound s)
{
using namespace std::chrono;
setAlarm(steady_clock::now() + hours(1), s, seconds(30));
;
然后我们撰写对应的std::bind
的版本
auto setSoundB =
std::bind(setAlarm,
steady_clock::now() + 1h, // 错误!见下文
_1,
30s);
steady_clock::now() + 1h
作为实参被传递给了std::bind
,而非setAlarm
。意味着表达式在评估求值时候是在调用std::bind
时刻。最终导致的结果是,警报设定的启动时刻是在调用std::bind
的时刻之后的一个小时,而非setAlarm
的时候的一个小时。解决办法是在原来的std::bind
里嵌套第2层std::bind
调用:
auto setSoundB =
std::bind(setAlarm,
std::bind(std::plus<>(), steady_clock::now(), 1h),
_1,
30s);
一旦对setAlarm
实施了重载,会产生新的问题:
enum class Volum {Normal, Loud, LoudPlusPlus };
void setAlarm(Time t, Sound s, Duration d, Volume v);
之前的lambda
式会一如既往运作,但是对于std::bind
的调用,无法通过编译了
auto setSoundB =
std::bind(setAlarm,
std::bind(std::plus<>(), steady_clock::now(), 1h),
_1,
30s);
因为编译器无法确定应该將哪个setAlarm
版本传递给std::bind
。它拿到的所有信息就只有一个函数名,而仅有函数名本身是多义的。解决办法是setAlarm
必须强制转型到适当的函数指针类型:
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);
在C++11
中std::bind
仅在两个受限的场合有着使用的理由:
- 移动捕获
- 多态函数对象