如何将 std::sqrt 用作 std::function?

How to use std::sqrt as std::function?

代码如下:

#include <iostream>
#include <cmath>
#include <functional>
#include <complex>

int main() {
    // This works.
    std::function<float(float)> f = [](auto const& x) {return std::sqrt(x);};   
    
    // This also works. Why this works?!
    using Complex = std::complex<double>;
    std::function<Complex(const Complex&)> g = std::sqrt<double>;
    
    // All of the following doesn't work.
    // error: conversion from ‘<unresolved overloaded function type>’
    // to non-scalar type ‘std::function<float(float)>’ requested
    std::function<float(float)> a = std::sqrtf<float>;
    std::function<float(float)> b = std::sqrt<float>;
    std::function<float(float)> c = std::sqrt;
    std::function<double(double)> d = std::sqrt<double>;
}

考虑到 std::sqrt reference,我真的很困惑为什么涉及复杂的一个有效,而其他的为什么不起作用。

我知道 ,但是,我对使用 std::complex 不感兴趣,而且,这个问题的 OP 特别要求 std::complex,相反,我只想使用 floatdouble(或实值,不复杂)。

这是怎么回事? 这样做的正确方法是什么?

Considering the std::sqrt reference,

您看错了std::sqrt页面:是非模板版本的页面。

如果您使用 std::sqrt<double>std::sqrt<float> 函数,则您使用的是 std::sqtr 模板 版本,在 this page.

如您所见,std::sqrt<T>

template< class T >
complex<T> sqrt( const complex<T>& z );

收到一个std::complex<T>和return一个std::complex<T>

所以当你写

std::function<float(float)> f = [](auto const& x) {return std::sqrt(x);}; 

之所以有效,是因为 lambda 调用(std::sqrt(x),其中 x 是一个 float)非模板函数。

写的时候

std::function<Complex(const Complex&)> g = std::sqrt<double>;

之所以有效,是因为 std::sqrt<double>std::sqrt 的模板版本,它收到 Complex const &std::complex<double> const &)和 return Complex const &

但是当你把东西写成

std::function<float(float)> b = std::sqrt<float>;
std::function<double(double)> d = std::sqrt<double>;

您将函数接收和 return 复杂传递给 std::function 等待函数接收和 return 简单(非复杂)浮点类型。

为了让它工作,你必须使用 std::sqrt 的非模板版本(所以没有 <float> 也没有 <double>)并转换正确的指针类型(到 select std::sqrt 非模板但重载版本的正确版本)。这也适用于 c.

std::function<float(float)> b = (float(*)(float))std::sqrt;
std::function<float(float)> c = (float(*)(float))std::sqrt;
std::function<double(double)> d = (double(*)(double))std::sqrt;

针对a的问题

std::function<float(float)> a = std::sqrtf<float>;

不一样;您必须删除模板部分 (<float>),因为 std::sqrtf 不是模板函数。

所以应该可以工作(std::sqrtf 没有超载,所以不需要强制转换,因为没有歧义)

std::function<float(float)> a = std::sqrtf;

不幸的是,我发现这不适用于 clang++ 和 g++。据我所知,这是因为 cmath 没有将 sqrtf 放入 std 命名空间(在我看来 g++ 和 clang++ 不符合要求)。

所以(使用 g++ 和 clang++)有效

std::function<float(float)> a = sqrtf;

std::sqrt()std::sqrtf()的浮点重载不是模板,所以这些形式是无效语法:

std::function<float(float)> a = std::sqrtf<float>;
// and
std::function<double(double)> d = std::sqrt<double>;

这个表格:

std::function<float(float)> c = std::sqrt;

没问题,只是 std::sqrt() 超载了,所以名称不能像未超载时那样退化为单个指针。

为了消除使用哪个重载的歧义,您需要将函数转换为正确的类型:

std::function<float(float)> works = static_cast<float(*)(float)>(std::sqrt);

但是如您所见,语法有点冗长,这就是为什么 lambda 版本是执行此操作的首选方法。

此表单有效的原因:

std::function<Complex(const Complex&)> g = std::sqrt<double>;

是因为std::complex版本的std::sqrt()是模板,其中模板参数是复杂对象的底层类型。