我已经开始编写自己的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似乎都错了。
有人能帮助我理解有问题的错误吗?