代码之家  ›  专栏  ›  技术社区  ›  Ðаn

将类型附加到标量的首选机制?

  •  1
  • Ðаn  · 技术社区  · 8 年前

    [编辑:将米/码更改为foo/bar;这不是将米转换为码。]

    将类型附加到标量(如 double ? 典型的用例是度量单位(但我不是在寻找实际的实现, boost has one ).

    这看起来很简单,如下所示:

    template <typename T>
    struct Double final
    {
        typedef T type;
        double value;
    };
    
    namespace tags
    {
        struct foo final {};
        struct bar final {};
    }
    constexpr double FOOS_TO_BARS_ = 3.141592654;
    inline Double<tags::bar> to_bars(const Double<tags::foo>& foos)
    {
        return Double<tags::bar> { foos.value * FOOS_TO_BARS_ };
    }
    
    static void test(double value)
    {
        using namespace tags;
        const Double<foo> value_in_foos{ value };    
        const Double<bar> value_in_bars = to_bars(value_in_foos);
    }
    

    真的是这样吗?或者,这种方法是否存在隐藏的复杂性或其他重要考虑因素?

    这似乎远比

       inline double foos_to_bars(double foos)
       {
          return foos * FOOS_TO_BARS_;
       }
    

    而几乎不增加任何复杂性或开销。

    3 回复  |  直到 8 年前
        1
  •  5
  •   krzaq    8 年前

    我会采用基于比率的方法,就像 std::chrono (Howard Hinnant在他最近的2016年C++大会上展示了这一点。) talk about <chrono> )

    template<typename Ratio = std::ratio<1>, typename T = double>
    struct Distance
    {
        using ratio = Ratio;
        T value;
    };
    
    template<typename To, typename From>
    To distance_cast(From f)
    {
        using r = std::ratio_divide<typename To::ratio, typename From::ratio>;
        return To{ f.value * r::den / r::num };
    }
    
    using yard = Distance<std::ratio<10936133,10000000>>;
    using meter = Distance<>;
    using kilometer = Distance<std::kilo>;
    using foot = Distance<std::ratio<3048,10000>>;
    

    demo

    赞成的意见:

    • meter m = yard{10} 是编译时错误或安全隐式转换,
    • 漂亮的类型名称,您必须非常努力地使用解决方案来进行无效转换

    欺骗:

    • 可能的整数溢出/精度问题(可通过实施质量缓解?)
        2
  •  1
  •   ROX    8 年前

    首先,是的,我认为你所建议的方式是相当合理的,尽管是否被优先考虑取决于上下文。 这种方法的优势在于,您定义的转换可能不仅仅是简单的乘法(例如摄氏度和华氏度)。

    然而,您的方法确实创建了不同的类型,这导致需要创建转换,这可能是好的,也可能是坏的,具体取决于使用情况。

    (我很欣赏你的码和米只是一个例子,我也将用它作为一个例子)

    如果我正在编写处理长度的代码,(大多数)无论单位是什么,逻辑都是相同的。虽然我可以将包含该逻辑的函数设置为模板,以便它可以采用不同的单元,但仍然有一个合理的用例,其中数据需要来自两个不同的源,并提供给不同的单元。在这种情况下,我宁愿处理一个长度类,而不是每个单位的一个类,这些长度可以保存它们的转换信息,也可以只使用一个固定单元,在输入/输出阶段进行转换。

    另一方面,当我们有不同的测量类型时,例如长度、面积、温度。在这些类型之间没有默认转换是一件好事。好的是,我不能不经意地给温度增加一个长度。

    (当然,类型的乘法是不同的。)

        3
  •  -2
  •   Bathsheba    8 年前

    在我看来,您的方法过于设计,以至于很难发现错误。即使在这一点上,您引入的语法复杂性也使您的转换变得不准确:您是从小数点后第8位开始的。

    标准换算为1英寸为25.4mm,这意味着一码正好是0.9144m。

    在IEEE754二进制浮点中,这一点及其倒数都不能精确表示。

    如果我是你,我会定义

    constexpr double METERS_IN_YARDS = 0.9144;
    
    constexpr double YARDS_IN_METERS = 1.0 / 0.9144;
    

    为了避免错误,以老式的方式使用双精度浮点运算。