问题描述
我确实希望能够在不声明 lambda 的情况下使用 extFunction 或 std::max
或 std::min
作为 square 方法的参数:
template<typename T>
T extFunction(T a,T b)
{
return a;
}
class Stuff
{
public:
template <typename F>
int square(int num,int num2,F&& func)
{
return func(num,num2);
}
};
int main()
{
Stuff s;
std::cout << s.square(1,2,std::max<int>) << std::endl;
return 0;
}
但是编译器 (gcc 11.1) 告诉我:
函数不明确:“无法推导出模板参数'F
'”
有没有一种简单的方法可以在没有 lambda 的情况下做到这一点?
编辑:
也许展示如何使用 lambdas 做到这一点会很有趣:
std::cout << s.square(1,[](auto&& a,auto&& b){return std::max(a,b);}) << std::endl;
std::cout << s.square(1,auto&& b){return std::min(a,auto&& b){return extFunction(a,b);}) << std::endl;
输出:
Program returned: 0
Program stdout
2
1
1
解决方法
您不应该获取标准库函数的地址。详情请看这里:
Can I take the address of a function defined in standard library?
因此,除了将 std::max
打包到函数对象(即 lambda、函子)或函数中之外,没有其他简单的方法。
std::max
和 std::min
的问题在于它们模棱两可,正如您所看到的 here。有几个重载,您必须告诉编译器您想使用哪一个。
-
在现代 C++ 中,您实际上会将所需的重载包装到 lambda 或 函子 中。
-
如果由于某种原因您无法使用 lambda 的旧代码(例如 C++03),您可以执行
static_cast
来告诉使用如下重载的编译器template <typename F,typename... Args> auto func(F f,Args... args) { return f(args...); } int main() { std::cout << func(static_cast<double const&(*)(double const&,double const&)>(std::max),3.0,2.0) << std::endl; return EXIT_SUCCESS; }
正如用户 JeJo 已经提到的,这被认为是不良做法(自 C++20 起),可能导致未指定的 (not undefined!) 行为,如
std::min
和std::max
不可寻址。 他所指的标准部分最近才在基于 this draft 的 C++20 中引入。可以在 here 上找到用户 Barry 发布的另一篇关于此的帖子。
在 C++ 中,当你想传递一个 invocable 时,它是多态的,因为它有多个潜在的签名,最好的方法是将它包装在一个对象中。重载集不是一等公民。不过有proposals。
当可调用对象是您提供给用户的东西时,将其定义为对象很简单:
inline constexpr auto extFunction = []<typename T>(T a,T b) -> T {
return a;
};
当你有一个重载集时,你必须把它包装起来。这很烦人,但您可以使用宏减少样板:
#define FWD(x) static_cast<decltype(x)&&>(x)
#define RETURNS(expr) noexcept(noexcept(expr)) -> decltype(expr) { return expr; }
#define OVERLOADS_OF(name) [&](auto&& ...args) RETURNS(name(FWD(args)...))
您的示例如下所示:
std::cout << s.square(1,2,OVERLOADS_OF(std::max)) << std::endl;
std::cout << s.square(1,OVERLOADS_OF(std::min)) << std::endl;
std::cout << s.square(1,extFunction) << std::endl;
为了完整起见,在相当狭窄的情况下,接收者还可以定义函数指针重载:
int square(int num,int num2,int (*func)(int,int));
s.square(1,extFunction); // now works
模板推导是从一个函数指针开始的,所以extFunction
的模板参数是推导的。
正如其他答案中提到的,对于标准库,您要避免依赖大多数标准库函数的特殊性,因为标准只承诺您可以调用它们,并不保证它们的形式。