Let the return type of a function auto foo(T f)
be the same as when calling sin(f)
from header cmath
in cases where f
is an intrinsic datatype:
template <typename T>
auto foo(T f) -> decltype(sin(f))
{
using std::sin;
return sin(f);
}
This is broken. The sin(f)
within decltype
is not looked up within std
, hence only the C
variant sin(double)
is found, whose return type is double
. The following program demonstrates that:
#include <cmath>
#include <iostream>
#include <typeinfo>
namespace meh {
struct Nanometer {};
struct SinfulNanometer {};
SinfulNanometer sin(Nanometer) { return SinfulNanometer(); }
}
template <typename T>
auto foo(T f) -> decltype(sin(f))
{
using std::sin;
std::cout << typeid(decltype(sin(f))).name() << '\n';
}
int main () {
std::cout << typeid(decltype(foo(0.))).name() << '\n'
<< typeid(decltype(foo(0.f))).name() << '\n'
<< typeid(decltype(foo(meh::Nanometer()))).name() << '\n';
foo(0.);
foo(0.f);
foo(meh::Nanometer());
}
Output:
d
d
N3meh15SinfulNanometerE
d
f
N3meh15SinfulNanometerE
The output suggests that the return type is always double
for foo->float
and foo->double
, only within foo()
, the correct sin(float|double)
is found because of the using std::sin
, which imports all overloads.
I wonder if such case was taken into consideration in the gremium already, but that's not my question.
My question is:
What is a sensible way to let foo
have the same return-type as a function whose name is either in namespace std
or ADL-looked-up?
Non working workaround:
template <typename T>
auto foo(T f) -> decltype(std::sin(f)); // Will miss sin(Nanometer)
Standard proposal?
template <typename T>
auto foo(T f) -> decltype(using std::sin; sin(f));
Using permutations of enable_if
is in the way of code being self-documenting. enable_if
is a nice exercise of metaprogramming. But to get a quick grasp of a seemingly simple function, imho not the right thing, therefore not in the spirit of maintainability.
edit: I am also using decltype
to use SFINAE less obscurely, so C++14 with it's new auto foo() {....}
declarations might not be of help.
You can use a detail namespace and some wrapping:
namespace detail {
using std::sin;
template<typename T>
auto foo(T f) -> decltype(sin(f)) { return sin(f); }
}
template<typename T>
auto foo(T f) -> decltype(detail::foo(f)) { return detail::foo(f); }