`decltype`并将ADL查找与非ADL查找混合

Seb*_*ach 4 c++ decltype argument-dependent-lookup c++11

测试用例

在一个内部数据类型的情况下,让函数的返回类型与从头auto foo(T f)调用时相同:sin(f)cmathf

template <typename T>
auto foo(T f) -> decltype(sin(f))
{
    using std::sin;
    return sin(f);
}
Run Code Online (Sandbox Code Playgroud)

这已破了.该sin(f)范围内decltype没有抬头之内std,因此只C变种sin(double)被发现,它的返回类型为double.以下程序演示了:

#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());
}
Run Code Online (Sandbox Code Playgroud)

输出:

d
d
N3meh15SinfulNanometerE
d
f
N3meh15SinfulNanometerE
Run Code Online (Sandbox Code Playgroud)

输出表明返回类型始终double为for,foo->float 并且 foo->double 其中找到foo()正确sin(float|double)using std::sin,因为,导入所有重载.

我想知道这种情况是否已经考虑过了,但这不是我的问题.

我的问题是:

有一个明智的方法是让foo一个返回类型与一个名字在其中namespace std或ADL查找的函数相同?

非工作解决方法:

template <typename T>
auto foo(T f) -> decltype(std::sin(f)); // Will miss sin(Nanometer)
Run Code Online (Sandbox Code Playgroud)

标准提案?

template <typename T>
auto foo(T f) -> decltype(using std::sin; sin(f));
Run Code Online (Sandbox Code Playgroud)

使用排列enable_if是代码自我记录的方式.enable_if是一个很好的元编程练习.但是为了快速掌握一个看似简单的功能,imho不是正确的事情,因此不具备可维护性的精神.


编辑:我也decltype使用SFINAE不那么模糊,所以带有它的新auto foo() {....}声明的C++ 14 可能没有帮助.

rig*_*old 9

您可以使用详细命名空间和一些包装:

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); }
Run Code Online (Sandbox Code Playgroud)

  • +1基本上是一个很好的解决方案,除了名称`detail`,因为这可能会在大型项目中经常使用,你只需要注入`使用std :: sin;` - 这就是我使用更具体的命名空间名称的原因这种情况包含`using`. (4认同)
  • 哦,是的,那太好了.一切都可以通过一个间接来解决:P (2认同)

Dan*_*rey 7

您需要一个级别的间接:

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)
{
    // ...
}
Run Code Online (Sandbox Code Playgroud)