#include <iostream>
#include <functional>
template <typename ... Args>
int invoke(const std::function<int(Args ..., bool)>& f, Args&& ... args) {
int r0 = f(std::forward<Args>(args)..., false);
int r1 = f(std::forward<Args>(args)..., true);
return r0 + r1;
}
int g(int a, int b, bool c) {
if (c) {
return a + b;
}
return a - b;
}
int main() {
int r = invoke(std::function<int(int,int,bool)>(g), 2, 3);
std::cout << r << std::endl;
}
上面的例子在 GCC 里类型推导不出来。如果 invoke
的 f
是 const std::function<int(bool, Args ...)>&
则可以推导出来,但像这种附加的参数在 Args 后面的情况怎么办呢?
1
GeruzoniAnsasu 2022-07-02 07:58:46 +08:00
应该无解,不如说这看着像个缺陷,不应该合法才对
无论是可变参数 va_args 也好,还是可变模板参数 typename ... TS 也好,本来都只能作为最后一个参数。这个 case 推导不出来不奇怪,但居然没报错 |
2
geelaw 2022-07-02 08:03:08 +08:00 1
你可以用 SFINAE 的思路
#include<utility> #include<functional> template <typename TCallable, typename ...TArgs> auto invoke_functor(TCallable &&f, TArgs&& ...args) -> std::enable_if_t<std::is_same_v<decltype(f(std::forward<TArgs>(args)..., std::declval<bool>())), int>, int> { int r0 = f(std::forward<TArgs>(args)..., false); int r1 = f(std::forward<TArgs>(args)..., true); return r0 + r1; } template <typename TCallable, typename ...TArgs> auto invoke_function(const TCallable &f, TArgs&& ...args) -> std::enable_if_t<std::is_same_v<TCallable, std::function<int(TArgs..., bool)>>, int> { int r0 = f(std::forward<TArgs>(args)..., false); int r1 = f(std::forward<TArgs>(args)..., true); return r0 + r1; } int g(int a, int b, bool c) { return c ? a + b : a - b; } #include<iostream> int main() { std::cout << invoke_functor(g, 2, 3) << std::endl; std::cout << invoke_function(std::function<int(int, int, bool)>(g), 2, 3) << std::endl; // Does not work. // std::cout << invoke_function(g, 2, 3) << std::endl; } |
3
Contextualist OP @GeruzoniAnsasu (我同事说 clang 可过编译🤦♂️)对,语法上确实说不过去,但是语义上来说,确实有这个需求。一般来说,可变参数一般用于“其余的可选参数”,但在这里我想表达更类似宏:对于一组参数,搭配不同的可选参数遍历执行。
@geelaw 确实可行,type traits 魔法,学习了 |
4
dangyuluo 2022-07-02 09:07:23 +08:00
@geelaw `invoke_function`里 SFINAE 的方法是比较两个`std::function`是否相同,请问比`invoke_functor`里检查返回值好在哪里呢。
另添加了个`std::is_invokable`的例子 https://godbolt.org/z/1jf5Ej9v4 |
5
geelaw 2022-07-02 09:14:44 +08:00
@dangyuluo 因为可调用的东西不一定是 std::function ,还可以是 lambda 表达式、函数之类的。从 lambda 表达式、函数建立 std::function 有额外的内存分配,对性能不好。此外你也看到了 invoke_function 不能接受函数的名字,必须手工构造一个 std::function 。
更好的思路是检查这个东西是否可以赋值给 int ,用 std::is_same_v< std::declval<int &>() = f(std::forward<TArgs>(args)..., std::declval<bool>()), int & > 这样也可以接受返回 const int &、int &、short 等的可调用的东西。 |