Возможная ошибка ADL в Visual Studio 2013

Следующий упрощенный код не компилируется в 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...

Любые идеи? Спасибо!


person dats    schedule 13.05.2015    source источник
comment
Это должно быть связано с выводом шаблона, потому что если я добавлю перегрузку bool isnan(const Derived& x)в пространство имен mineто все будет работать.   -  person dats    schedule 13.05.2015
comment
Просто для ясности: добавление перегрузок для каждой функции и каждого класса, производного от 'mine::CRTP', не является жизнеспособным решением, так как много функций и много производных классов... :(   -  person dats    schedule 13.05.2015


Ответы (1)


Судя по тому, что я вижу, это не ошибка.

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 может находиться в глобальном пространстве имен (из math.h), а не только в std (cmath), что вызывает конфликт. - Источник

person David Ruhmann    schedule 13.05.2015
comment
Я только что узнал здесь реализация позволяет [...] вносить ‹isnan› в глобальное пространство имен Все время я думал, что isnan находится в пространстве имен std. Теперь ваш ответ имеет смысл. Если вы проясните этот момент (иснан может находиться в глобальном пространстве имен), я приму ваш ответ. - person dats; 15.05.2015