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?
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;