在没有 lambda 的情况下将模板化函数作为方法参数传递?

Passing a templated function as method argument without lambdas?

我确实希望能够在不声明 lambda 的情况下使用 extFunction 或 std::maxstd::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) 告诉我:

the function is ambiguous : "couldn't deduce template parameter 'F'"

有没有不用 lambda 的简单方法?

编辑:

也许展示如何使用 lambda 执行此操作会很有趣:

std::cout << s.square(1,2,[](auto&& a, auto&& b){return std::max(a,b);}) << std::endl;
    
std::cout << s.square(1,2,[](auto&& a, auto&& b){return std::min(a,b);}) << std::endl;
    
std::cout << s.square(1,2,[](auto&& a, auto&& b){return extFunction(a,b);}) << std::endl;

输出:

Program returned: 0
Program stdout

2
1
1

您不应该获取标准库函数的地址。详情请看这里:

因此,除了将 std::max 打包到函数对象(即 lambda、仿函数)或函数中之外,没有其他简单的方法。

std::maxstd::min 的问题在于它们 模棱两可 如您所见 here。有几个重载,你必须告诉你的编译器你想使用哪一个。


  • 在现代 C++ 中,您实际上会将希望的重载包装到 lambdafunctor.

  • 如果由于某种原因您无法使用 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;
    }
    

    Try it here!

    正如用户 JeJo 已经提到的,这被认为是 不好的做法(C++20 起),可能会导致未指定的 (not undefined!) 行为,因为 std::minstd::max 不可寻址。 他所指的那部分标准最近才在C++20中引入,基于this draft. Another post by user Barry about this can be found .

在 C++ 中,当你想要传递一个可调用对象时,它是多态的,因为它具有多个潜在的签名,最好的方法是将它包装在一个对象中。超载集不是第一个 class 公民。不过有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, 2, OVERLOADS_OF(std::min)) << std::endl;
std::cout << s.square(1, 2, extFunction) << std::endl;

为了完整起见,在相当狭窄的情况下,接收方还可以定义函数指针重载:

int square(int num, int num2, int (*func)(int, int));
s.square(1, 2, extFunction); // now works

模板推导基于函数指针,因此推导了extFunction的模板参数。

正如其他答案中提到的,对于标准库,你要避免依赖大多数标准库函数的特殊性,因为标准只承诺你可以调用它们,并不保证它们的形式。