为何std::function接受不匹配签名及如何限制仅允许精确类型匹配
问题1:为什么签名不同的
std::function可以隐式转换? std::function的设计本身就支持兼容签名的隐式转换,并不要求类型完全相同,转换规则如下:
- 待转换的可调用对象调用时,接收的参数可以隐式转换为目标
std::function签名的参数类型 - 目标
std::function的返回值为void时,可直接丢弃原可调用对象的返回值;非void返回值的场景下,要求原可调用对象的返回值可以隐式转换为目标返回值类型
你示例中std::function<bool(double)>完全符合std::function<void(double)>的兼容要求:传入double参数的调用逻辑完全匹配,返回的bool值在目标签名下直接丢弃即可,因此可以正常隐式转换。std::is_same_v判断的是类型完全相等,自然会返回false,和隐式转换的合法性不冲突。
问题2:编译期禁止非精确匹配的转换方案
可以通过自定义类型包装、增加静态断言约束的方式实现仅允许精确类型赋值,以下是两种常见实现:
方案1:给结构体S增加赋值约束
直接修改S的赋值运算符,添加静态类型校验:
#include <type_traits> #include <utility> template <typename T> struct S { T function; // 仅允许精确匹配的类型赋值 template <typename U> S& operator=(U&& value) { static_assert( std::is_same_v<std::decay_t<U>, T>, "Only exact matching function type is allowed for assignment" ); function = std::forward<U>(value); return *this; } };
修改后如果给S<std::function<void(double)>>赋值std::function<bool(double)>类型的值,会直接触发静态断言编译失败。
方案2:封装精确匹配的std::function wrapper
如果需要在更多场景复用精确匹配的逻辑,可以封装独立的包装类型:
#include <functional> #include <type_traits> template <typename Sig> class ExactFunction : public std::function<Sig> { public: using Base = std::function<Sig>; // 允许精确类型的构造和赋值 ExactFunction(Base f) : Base(std::move(f)) {} ExactFunction& operator=(Base f) { Base::operator=(std::move(f)); return *this; } // 删除所有非精确类型的构造和赋值 template <typename OtherSig> ExactFunction(std::function<OtherSig>) = delete; template <typename OtherSig> ExactFunction& operator=(std::function<OtherSig>) = delete; };
使用时把S的成员类型改为ExactFunction<void(double)>即可自动实现编译期校验。
内容的提问来源于stack exchange,提问作者Juergen
相关产品推荐
相关产品推荐

