2011-05-31 29 views
1

当阅读<ratio><chrono>时,我试图想象一个Length类型,它可以防止意外转换错误。C++ 0x使用<ratio>更安全长度类型

这是我得到:

#include <iostream> 
#include <ratio> 
using namespace std; 

template<typename Scale> 
struct Length { 
    long long val_; 
    Length(long long val) : val_{val} {} 
    Length() = default; 
    Length(const Length&) = default; 
    Length& operator=(const Length&) = default; 
    // conversion 
    template<typename Scale2> 
    Length(const Length<Scale2> &other) 
    : val_{ other.val_*(Scale2::num*Scale::den)/(Scale2::den*Scale::num) } 
    { } 
    // access 
    long long value() const { return val_; } 
}; 
typedef Length<ratio<1>> m; 
typedef Length<kilo> km; 
typedef Length<milli> mm; 
typedef Length<ratio<1000,1094>> yard; 

要这样使用

int main() { 
    km len_km = 300; 
    mm len_mm = len_km; 
    cout << " millimeter:" << len_mm.value() << endl; 
    cout << " m:" << m{len_km}.value() << endl; 
    cout << " yd:" << yard{len_km}.value() << endl; 
} 

现在我可以添加所有的+*操作获得真正舒适... :-)

我想知道:

  • 是否有一个更容易访问算术设施durationtime_point定义在<chrono>无论如何?我可以使用这些减少Length的努力吗?
  • 编译时常量(Scale2::num*Scale::den)/(Scale2::den*Scale::num)转换构造函数(fraction/underflow?)中似乎很危险,但我无法找到更好的元编程方式,这里的任何提示?

回答

2
  • 是否有可以 算术设施是 durationtime_point定义 <chrono>反正一个更容易获得?我可以使用那些 减少Length的努力吗?

对于“混合模式”算术和比较,您可以采取的common_type<T1, T2>::type优势定义返回类型。 duration专业common_type是Period1和Period2的最大公约数,其中Period1和Period2是参与算术或比较操作的两个ratio。你可以使用它像:

template <typename Scale1, typename Scale2> 
    typename std::common_type<Length<Scale1>, Length<Scale2>>::type 
    operator+(Length<Scale1> x, Length<Scale2> y); 

不幸的是,你必须重塑如何在编译时得到两个ratio S上的最大公约数。开始于编译时gcd和lcm元函数的无符号long long。

嗯...或者你也许可以将你的专业化common_type基于已经完成的duration。您可以将结果durationperiod重新解释为您的Length的比例因子。我没有原型,只是一个想法。

  • 的编译时间常数 (Scale2::num*Scale::den)/(Scale2::den*Scale::num)转换 构造似乎危险(分/下溢?), 但我想不出更好的 元编程的方式,在这里任何提示?

同意。 duration处理这个问题有:

template <class Rep2, class Period2> 
    constexpr duration(const duration<Rep2, Period2>& d); 

备注:此构造不得 参与重载决议 除非treat_as_floating_point<rep>::value是真实的或两者 ratio_divide<Period2, period>::den是是假的。 [注意:此需求在 在基于积分的 持续时间类型之间转换时可防止 隐式截断错误。这样的构造很容易导致关于持续时间的值的混淆。 - 尾注]

I.e.你需要enable_if你的Length转换构造函数,这样它才存在,如果转换是准确的(如果你想根据你的整数类型的长度)。对于准确的转换,转换因子(Scale2::num*Scale::den)/(Scale2::den*Scale::num)必须是可计算的,不能除法(除1除外)。您可以使用ratio_divide为您执行此除法,然后得到的分母必须为1(用于精确转换)。

enable_if<ratio_divide<Scale2, Scale1>::type::den == 1, ...> 

这是一个很好的学习项目ratio!玩的开心! :-)

+0

我仍然在我有趣的项目列表中。你在这里给了我一个很好的支持。我可以在大多数地方跟踪你,我同意需要* gcd *。编译时* gcd *对我来说已经是一个相当大的挑战了,我猜(constexpr不能递归,对吗?单线非递归循环gcd计算在哪里?叹*)。我还没有找到时间。但我会......我会的。 – towi 2011-10-10 19:51:02

+0

这个代码是免费使用,但你想要的只是保留它的版权:http://llvm.org/svn/llvm-project/libcxx/trunk/include/ratio – 2011-10-10 20:56:50

1

您使用整数类型来表示物理量。这不是人们通常想要的。如果你坚持一个整数类型,至少要按照正确的顺序进行乘法和除法,即首先进行所有乘法运算,然后再进行除法运算(比较100 *(255/256)和(100 * 255)/ 256)。

在相关说明中,请记住,您将长度乘以长度,您将得到一个区域,而不是长度。考虑到这一点的实际图书馆存在,例如见siunits

+0

好点,是的。我的破坏意图是在运行时只需要一次乘法就可以获得一个编译时间常量。但是你是对的,这里用'int'就是坏的。但现在我将以此为例。而且,我听说过SIUnits-lib,并从那里获得了灵感。但我只想解释新的C++ 0x-Stdlib - 我试图找到一个使用''而没有''的例子。你也是对的,我可能会忘记只用标量符号来操作operator *()。唉,* siunit *没有成为新的标准。真可惜,不是吗? – towi 2011-05-31 10:25:44

+1

当100,255和256是编译时常量,并且想要最小化溢出的可能性时,首先要计算出分子和分母的公因子。例如gcd(100,256)是4.所以你可以将100和256除以4.现在你的问题是(25 * 255)/ 64。这个问题不能进一步减少,所以最终的答案是(以合理的形式)6375/64。这是'ratio_divide'为你所做的,并且在编译时都是这样。此外,如果溢出是不可避免的(即使是gcd的因素),你会发现在编译时,而不是运行时。 – 2011-05-31 16:46:53