문제

Testcase

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.

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

다른 팁

You need one level of indirection:

namespace find_sin
{
    using std::sin;
    template<typename T>
    using type = decltype(sin(std::declval<T>()));
}

template <typename T>
find_sin::type<T> foo(T f)
{
    // ...
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top