以下简化代码无法在VS2013下编译:

#include <cmath>
namespace mine
{
    template <typename A>
    struct Base
    {
        double value() const { return static_cast<const A&>(*this).value(); }
    };

    struct Derived : Base < Derived >
    {
        Derived(double x) : m_val(x) {}
        double value() const { return m_val; }
        double m_val;
    };

    template <typename A>
    bool isnan(const Base<A>& x) { return ::isnan(x.value()); }

    struct ItWorks
    {
        double value() const { return 3.14; }
    };
    bool isnan(ItWorks t) { return ::isnan(t.value()); }
}

int main()
{
    mine::Derived d(2.0);
    bool b = isnan(d); // this one fails in VS2013

    mine::ItWorks t;
    bool bb = isnan(t); // this one works

    return 0;
}

错误是:
c:\program files (x86)\microsoft visual studio 12.0\vc\include\math.h(425): error C2665: 'fpclassify' : none of the 3 overloads could convert all the argument types
could be 'int fpclassify(long double)'
or       'int fpclassify(double)'
or       'int fpclassify(float)'
while trying to match the argument list '(mine::Derived)'

我曾期望ADL在mine::isnan()上被调用时能够踢进mine::Derived,但是由于某些原因,VS2013试图从全局命名空间中调用isnan()模板函数。

当然,如果我直接调用mine::isnan(),一切都可以正常工作,但这不能解决我的问题,因为我需要在模板化的上下文中调用isnan(),在那里我可能会得到double或从mine::CRTP派生的任何类。

它必须与模板推导有一些交互,因为一切都按预期对mine::ItWorks起作用:这是一个不使用CRTP的简单结构。

但是,gcc 5.1.0和clang 3.5.1都同意我的观点,并且可以正确编译代码。这似乎是VS2013错误...

有任何想法吗?
谢谢!

最佳答案

从我看来,这似乎不是一个错误。

template<class _Ty> inline __nothrow bool isnan(_Ty _X)

template<typename A> bool isnan(const Base<A>& x)

这些功能将分别解析为
bool isnan(Derived _X)

bool isnan(const Base<Derived>& x)

因此,当给isnan一个Derived类型时,它将匹配显式使用Derived的函数定义。并且由于fpclassify无法处理Derived而发生错误。

而不是尝试覆盖具有模板变量类型的isnan,而是覆盖fpclassify函数。
template <typename A>
int fpclassify(const Base<A>& x)
{
    return ::fpclassify(x.value());
}

然后您的实现将起作用。

来自评论的更新
isnan可能位于全局 namespace 中(来自math.h),而不仅仅是位于std(cmath)中,这会导致冲突。 -Source

关于c++ - Visual Studio 2013中可能的ADL错误,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/30210095/

10-14 16:13