Search code examples
c++functiontemplatesfunction-pointers

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


Here's the code:

#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>;
}

Considering the std::sqrt reference, I am really confused as to why the one involving complex works, and why the other ones do not work.

I am aware of this question, but, I am not interested in using std::complex, and, the OP from this question specifically asks for std::complex, and, in contrast, I'd like to work only with float or double (or real valued, not complex).

What's going on? What's the correct way of doing this?


Solution

  • Considering the std::sqrt reference,

    You're looking the wrong std::sqrt page: it's the page of the non-template version.

    If you use std::sqrt<double> and std::sqrt<float> functions, you're using the template version of std::sqtr, that is referenced in this page.

    As you can see, std::sqrt<T>

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

    receive a std::complex<T> and return a std::complex<T>.

    So when you write

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

    works because the lambda call (std::sqrt(x), where x is a float) the not template function.

    When you write

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

    works because std::sqrt<double> is the template version of std::sqrt that receive a Complex const & (std::complex<double> const &) and return a Complex const &

    But when you write something as

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

    you pass function receiving and returning a complex to std::function waiting for function receiving and returning a simple (not complex) floating point type.

    To make it works, you have to use the non-template version of std::sqrt (so no <float> and no <double>) and cast the right pointer type (to select the right version of the std::sqrt non-template but overloaded version). This works also for 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;
    

    For a the problem

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

    is different; you have to remove the template part (<float>), given that std::sqrtf isn't a template function.

    So should works (std::sqrtf isn't overloaded, so no cast should be required, given there isn't ambiguity)

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

    Unfortunately I see that this doesn't works with clang++ and with g++. As far I understand it's because cmath doesn't put sqrtf inside the std namespace (and seems to me that g++ and clang++ are not conforming).

    So (with g++ and clang++) works

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