如何检查可调用对象的签名(含参数与返回类型)并避免返回值丢失?
问题描述
- 存在两类更新变量的函数:
- 返回变化值,由客户端代码自行应用(比如
calculate函数); - 通过引用直接修改传入的值(比如
update函数)。
- 返回变化值,由客户端代码自行应用(比如
- 目标是统一调用这两类函数,但客户端开发者误写了接受
float&参数且带有返回值的lambda,导致process函数进入错误分支,返回值直接丢失。 - 需求:在
process函数中添加编译时检查——若传入的可调用对象接受float&类型参数,其返回类型必须为void,要求实现编译时错误(而非运行时断言),且不能使用包装器。
编译时检查实现方案
可以利用C++20的requires表达式和类型特性,结合静态断言或SFINAE重载来实现编译期校验,以下是两种可行方案:
方案1:静态断言(直观报错)
先定义两个编译期常量,分别判断可调用对象是否接受float&,以及接受float&时返回类型是否为void:
#include <type_traits> template<typename F> constexpr bool accepts_float_ref = requires(F f, float& val) { f(val); }; template<typename F> constexpr bool returns_void_for_float_ref = accepts_float_ref<F> && std::is_same_v<decltype(std::declval<F>()(std::declval<float&>())), void>;
然后在process函数中,针对接受float&的分支添加静态断言:
template<typename Func> void process(float& value, Func func) { if constexpr (accepts_float_ref<Func>) { static_assert(returns_void_for_float_ref<Func>, "Error: Callable that takes float& must return void"); func(value); } else { value += func(); } }
当客户端传入错误的lambda(接受float&却返回非void类型)时,编译器会直接触发静态断言,输出清晰的错误提示。
方案2:SFINAE重载(严格匹配分支)
通过requires约束来限定两个重载分支的匹配条件,确保只有符合要求的可调用对象能进入对应分支:
#include <type_traits> template<typename F> constexpr bool accepts_float_ref = requires(F f, float& val) { f(val); }; // 分支1:仅匹配接受float&且返回void的可调用对象 template<typename Func> requires accepts_float_ref<Func> && std::is_same_v<decltype(std::declval<Func>()(std::declval<float&>())), void> void process(float& value, Func func) { func(value); } // 分支2:仅匹配不接受float&的可调用对象(返回变化值) template<typename Func> requires (!accepts_float_ref<Func>) void process(float& value, Func func) { value += func(); }
这种方式下,错误的lambda无法匹配任何一个重载,编译器会报“找不到匹配的process函数”的错误,从根源上阻止错误调用。
测试示例
// 合法的直接修改函数 void update(float& x) { x += 1.0f; } // 合法的返回变化值函数 float calculate() { return 2.0f; } int main() { float val = 0.0f; process(val, update); // 正常执行,val变为1.0f process(val, calculate); // 正常执行,val变为3.0f // 以下代码会触发编译错误(符合预期) // process(val, [](float& x) -> float { x += 3.0f; return 4.0f; }); }
内容的提问来源于stack exchange,提问作者Damir Tenishev
相关产品推荐
相关产品推荐

