【问题标题】:`decltype` and mixing ADL-lookup with non-ADL-lookup [duplicate]`decltype` 并将 ADL 查找与非 ADL 查找混合 [重复]
【发布时间】:2013-11-02 16:51:08
【问题描述】:

测试用例

如果f 是一个内在数据类型,让函数auto foo(T f) 的返回类型与从标头cmath 调用sin(f) 时相同:

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

这个坏了。 decltype 中的sin(f) 不会在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());
}

输出:

d
d
N3meh15SinfulNanometerE
d
f
N3meh15SinfulNanometerE

输出表明foo-&gt;float 的返回类型始终为double foo-&gt;double,仅 foo() 内,正确的sin(float|double) 是发现是因为 using std::sin,它会导入所有重载。

我想知道在 gremium 中是否已经考虑过这种情况,但这不是我的问题。

问题

我的问题是:

foo 具有与名称在namespace std 或ADL-looked-up 中的函数相同的返回类型的合理方法是什么?

无效的解决方法:

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

标准提案?

template <typename T>
auto foo(T f) -> decltype(using std::sin; sin(f));

使用enable_if 的排列会妨碍代码自我记录。 enable_if 是元编程的一个很好的练习。但是要快速掌握一个看似简单的功能,恕我直言,这不是正确的做法,因此不符合可维护性的精神。


编辑:我也在使用 decltype 来减少 SFINAE 的隐蔽性,因此 C++14 及其新的 auto foo() {....} 声明可能没有帮助。

【问题讨论】:

  • 在 C++14 中,您将能够只使用 auto;不需要decltype
  • @rightfold:这也可以作为 SFINAE 的替代品吗?现在,我也在使用decltype 进行 SFINAE 技术。
  • 我对这里的 SFINAE 不太确定。
  • @R.MartinhoFernandes:我想知道投票结束自己的问题是否值得徽章,因为我刚刚做到了:P

标签: c++ c++11 decltype argument-dependent-lookup


【解决方案1】:

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

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

【讨论】:

  • 哦,是的,这很好。一切都可以通过一种间接的方式来解决:P
  • +1 基本上是一个很好的解决方案,除了名称 detail 因为这可能会在较大的项目中经常使用,而您刚刚注入 using std::sin; - 这就是我使用更具体的原因此案例的命名空间名称包含using
  • @DanielFrey:我同意这一点,我只是在这里看到 detail 作为占位符。我将适当地命名它,例如 intrinsic_or_adlmixin_cmath_names 之类的东西。实际上,我说的是我为 RGB 三元组重载的整个 cmath 标头,现在我想将其推广到任何可数组索引的类型。
【解决方案2】:

你需要一级间接:

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)
{
    // ...
}

【讨论】:

  • 哦,是的,这很好。一切都可以通过一种间接的方式来解决:P 不幸的是,rightfold 快了大约 0.7 秒。
猜你喜欢
  • 1970-01-01
  • 2013-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-02
相关资源
最近更新 更多