如何实现类std::invoke行为的C++ LIFT宏?
问题描述
我希望实现一个类std::invoke行为的LIFT宏。目前已实现两种LIFT宏:
- 一种可将普通函数转为函数对象,代码如下:
#define LIFT(F) [&](auto&&... args) noexcept(noexcept((F)(std::forward<decltype(args)>(args)...))) -> decltype((F)(std::forward<decltype(args)>(args)...)) { return (F)(std::forward<decltype(args)>(args)...); }
可使用LIFT(std::max)作为函数对象。
- 另一种用于调用第一个参数的成员函数,代码如下:
#define LIFT_MEMFN(F) [&](auto&& self, auto&&... args) noexcept(noexcept(std::forward<decltype(self)>(self).F(std::forward<decltype(args)>(args)...))) -> decltype(std::forward<decltype(self)>(self).F(std::forward<decltype(args)>(args)...)) { return std::forward<decltype(self)>(self).F(std::forward<decltype(args)>(args)...); }
我想通过SFINAE技巧实现一个类std::invoke行为的LIFT宏:当传入第一个参数a0时,优先尝试a0.F(args...),再尝试a0->F(args...),最后回退到F(a0, args...)。请问这是否可行?
可行方案
完全可行,我们可以利用C的重载决议优先级结合SFINAE(或C20的requires表达式)来实现这一逻辑,最终的LIFT宏可以自动适配成员函数(对象调用、指针调用)和普通函数的场景,完全对齐std::invoke的行为。
实现思路
- 优先级排序:通过
constexpr if的分支顺序,让编译器优先检查并选择合法的调用形式:- 第一优先级:直接调用对象的成员函数
a0.F(args...) - 第二优先级:调用指针的成员函数
a0->F(args...) - 第三优先级:普通函数调用
F(a0, args...)
- 第一优先级:直接调用对象的成员函数
- 完美转发:用
std::forward保留参数的左值/右值特性,同时转发原函数的noexcept属性和返回值类型。 - 简洁性:使用C++20的
requires表达式替代传统SFINAE代码,逻辑更直观。
最终实现代码
#include <utility> #define LIFT(F) \ [&]<typename... Args>(Args&&... args) noexcept( \ noexcept( \ []<typename... Ts>(Ts&&... ts) -> decltype(auto) { \ if constexpr (requires { std::forward<Ts>(ts).*F(std::forward<Ts>(ts)...); }) { \ return std::forward<Ts>(ts).*F(std::forward<Ts>(ts)...); \ } else if constexpr (requires { std::forward<Ts>(ts)->*F(std::forward<Ts>(ts)...); }) { \ return std::forward<Ts>(ts)->*F(std::forward<Ts>(ts)...); \ } else { \ return F(std::forward<Ts>(ts)...); \ } \ }(std::forward<Args>(args)...) \ ) \ ) -> decltype(auto) { \ if constexpr (requires { std::forward<Args>(args).*F(std::forward<Args>(args)...); }) { \ return std::forward<Args>(args).*F(std::forward<Args>(args)...); \ } else if constexpr (requires { std::forward<Args>(args)->*F(std::forward<Args>(args)...); }) { \ return std::forward<Args>(args)->*F(std::forward<Args>(args)...); \ } else { \ return F(std::forward<Args>(args)...); \ } \ }
关键细节说明
requires表达式:直接判断某类调用是否合法,替代了传统的decltype+std::enable_if组合,代码可读性更强。decltype(auto):自动推导返回值类型,保留原函数的引用/值特性(比如原函数返回左值引用,生成的函数对象也会返回左值引用)。noexcept转发:通过内部lambda的noexcept推导,确保生成的函数对象的异常属性与原调用完全一致。- 优先级保证:
constexpr if的分支顺序直接决定了调用优先级,编译器会按顺序检查,选择第一个合法的调用形式。
使用示例
#include <iostream> #include <algorithm> struct Foo { int add(int a, int b) const noexcept { return a + b; } }; int multiply(int a, int b) noexcept { return a * b; } int main() { Foo f; Foo* pf = &f; // 调用成员函数(对象形式) std::cout << LIFT(&Foo::add)(f, 3, 4) << std::endl; // 输出7 // 调用成员函数(指针形式) std::cout << LIFT(&Foo::add)(pf, 5, 6) << std::endl; // 输出11 // 调用普通函数 std::cout << LIFT(multiply)(2, 3) << std::endl; // 输出6 // 调用标准库函数 std::cout << LIFT(std::max)(10, 20) << std::endl; // 输出20 }
内容的提问来源于stack exchange,提问作者Ben
相关产品推荐
相关产品推荐

