对于MSVC和AppleClang,未正确解析std :: common_type

时间:2016-11-07 20:44:10

标签: c++ macos c++11 visual-c++ clang++

我已经开始编写自己的typesafe unit library目标C ++ 14。主要是我在Linux上开发,所以我的编译器自然是gcc和clang。我对此已经走得很远,并决定将其扩展到其他平台(Windows和MSVC,OSX和AppleClang)。

麻烦的是,这些编译器都不会编译以下测试代码:

TEST_F(DistanceTest, OperatorRemainder_WhenGettingRemainderByDistance_WillGiveRemainder)
{
    auto const result = 2531_m % 1_km;
    EXPECT_EQ(531, result.count());
}

TEST_F(DistanceTest, OperatorRemainderInt_WhenGettingRemainderByDistance_WillGiveRemainder)
{
    auto const result = units::distance<int>{2531} % 1_km;
    EXPECT_EQ(531, result.count());
}

在MSVC中,我收到以下错误:

c:\program files (x86)\microsoft visual studio\vs15preview\Common7\IDE\VisualCpp\Tools\MSVC\14.10.24516.00\include\type_traits(1246): error C2446: ':': no conversion from 'units::kilometres' to 'double' [C:\Users\david.brown\units\.vs\build\test\distance_ut.vcxproj]
  c:\program files (x86)\microsoft visual studio\vs15preview\Common7\IDE\VisualCpp\Tools\MSVC\14.10.24516.00\include\type_traits(1246): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
  C:\Users\david.brown\units\test\test_distance.cpp(171): note: see reference to class template instantiation 'std::common_type<double,units::kilometres>' being compiled
c:\program files (x86)\microsoft visual studio\vs15preview\Common7\IDE\VisualCpp\Tools\MSVC\14.10.24516.00\include\type_traits(1247): error C2955: 'std::decay': use of class template requires template argument list [C:\Users\david.brown\units\.vs\build\test\distance_ut.vcxproj]
  c:\program files (x86)\microsoft visual studio\vs15preview\Common7\IDE\VisualCpp\Tools\MSVC\14.10.24516.00\include\type_traits(1056): note: see declaration of 'std::decay'
c:\program files (x86)\microsoft visual studio\vs15preview\Common7\IDE\VisualCpp\Tools\MSVC\14.10.24516.00\include\type_traits(1246): error C2446: ':': no conversion from 'units::kilometres' to 'std::ios_base::iostate' [C:\Users\david.brown\units\.vs\build\test\distance_ut.vcxproj]
  c:\program files (x86)\microsoft visual studio\vs15preview\Common7\IDE\VisualCpp\Tools\MSVC\14.10.24516.00\include\type_traits(1246): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
  C:\Users\david.brown\units\test\test_distance.cpp(177): note: see reference to class template instantiation 'std::common_type<int,units::kilometres>' being compiled

有问题的代码是:

namespace std
{
    template <typename CommonRep, typename Length1, typename Length2>
    struct distance_common_type
    {
    private:
        using gcd_num    = units::detail::greatest_common_divisor<Length1::num, Length2::num>;
        using gcd_den    = units::detail::greatest_common_divisor<Length1::den, Length2::den>;
        using common_rep = typename CommonRep::type;
        using ratio      = std::ratio<gcd_num::value, (Length1::den / gcd_den::value) * Length2::den>;

    public:
        using type = units::distance<common_rep, ratio>;
    };

    template <typename Rep1, typename Length1, typename Rep2, typename Length2>
    struct common_type<units::distance<Rep1, Length1>,
                       units::distance<Rep2, Length2>>
    {
        using type = typename distance_common_type<std::common_type<Rep1, Rep2>,
                                                   Length1,
                                                   Length2>::type;
    };
}

template <
        typename Rep1,
        typename Length1,
        typename Rep2,
        typename Length2>
    constexpr auto operator%(distance<Rep1, Length1> lhs, distance<Rep2, Length2> rhs)
        -> typename std::enable_if<std::is_floating_point<typename std::common_type<Rep1, Rep2>::type>::value,
                                   typename std::common_type<distance<Rep1, Length1>,
                                                             distance<Rep2, Length2>>::type>::type
    {
        using distance1   = distance<Rep1, Length1>;
        using distance2   = distance<Rep2, Length2>;
        using common_type = typename std::common_type<distance1, distance2>::type;

        return static_cast<common_type>(detail::fmod(static_cast<common_type>(lhs).count(),
                                                     static_cast<common_type>(rhs).count()));
    }

    template <
        typename Rep1,
        typename Length1,
        typename Rep2,
        typename Length2>
    constexpr auto operator%(distance<Rep1, Length1> lhs, distance<Rep2, Length2> rhs)
        -> typename std::enable_if<std::is_integral<typename std::common_type<Rep1, Rep2>::type>::value,
                                   typename std::common_type<distance<Rep1, Length1>,
                                                             distance<Rep2, Length2>>::type>::type
    {
        using distance1   = distance<Rep1, Length1>;
        using distance2   = distance<Rep2, Length2>;
        using common_type = typename std::common_type<distance1, distance2>::type;

        return static_cast<common_type>(static_cast<common_type>(lhs).count() % static_cast<common_type>(rhs).count());
    }

AppleClang因同样的原因https://travis-ci.org/bigdavedev/units/jobs/169334303#L100抱怨相同的代码。

我无法弄清楚哪组编译器是正确的。四分之二可以编译以上,而其他两个则不能。

由于第一个测试使用了两个单位:: distance的实例,我希望common_type特化看起来像:

std::common_type<units::distance<double, std::ratio<1>>, units::distance<double, std::kilo>>;

应该将公共代表解析为double,从而编译。在第二种情况下,我希望common_type特化看起来像:

std::common_type<units::distance<int, std::ratio<1>>, units::distance<double, std::kilo>>;

应将common_rep解析为double并进行编译。从逻辑上讲,MSVC和AppleClang似乎都错了。

有人能帮助我理解有问题的错误吗?

0 个答案:

没有答案