Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 将类型附加到标量的首选机制?_C++_Templates_Types_Units Of Measurement - Fatal编程技术网

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

C++ 将类型附加到标量的首选机制?,c++,templates,types,units-of-measurement,C++,Templates,Types,Units Of Measurement,[编辑:将米/码更改为foo/bar;这不是将米转换为码。] 将类型附加到标量(例如double)的最佳方式是什么?典型的用例是度量单位(但我不寻求实际的实现) 这似乎是一个简单的问题,如: template <typename T> struct Double final { typedef T type; double value; }; namespace tags { struct foo final {}; struct bar final

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

将类型附加到标量(例如
double
)的最佳方式是什么?典型的用例是度量单位(但我不寻求实际的实现)

这似乎是一个简单的问题,如:

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);
}

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

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

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

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

如果我是你,我会定义

constexpr double METERS_IN_YARDS = 0.9144;

constexpr double YARDS_IN_METERS = 1.0 / 0.9144;

为了避免出现bug,请使用老式的双精度浮点算法。

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

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

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

如果我是你,我会定义

constexpr double METERS_IN_YARDS = 0.9144;

constexpr double YARDS_IN_METERS = 1.0 / 0.9144;

为了避免这些错误,用老式的方法进行双精度浮点运算。

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

模板
结构距离
{
使用比率=比率;
T值;
};
模板
到距离(从f开始)
{
使用r=std::ratio_divide;
返回到{f.value*r::den/r::num};
}
使用码=距离;
使用米=距离;
使用公里=距离;
使用脚=距离;

这是一个幼稚的实现,可能会有很大的改进(至少在安全的情况下允许隐式强制转换),但这是一个概念证明,而且它的可扩展性很低

优点:

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

  • 可能的整数溢出/精度问题(可能通过实施质量得到缓解?)
  • 要正确地实施,可能非常重要

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

模板
结构距离
{
使用比率=比率;
T值;
};
模板
到距离(从f开始)
{
使用r=std::ratio_divide;
返回到{f.value*r::den/r::num};
}
使用码=距离;
使用米=距离;
使用公里=距离;
使用脚=距离;

这是一个幼稚的实现,可能会有很大的改进(至少在安全的情况下允许隐式强制转换),但这是一个概念证明,而且它的可扩展性很低

优点:

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

  • 可能的整数溢出/精度问题(可能通过实施质量得到缓解?)
  • 要正确地实施,可能非常重要

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

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

(我很感激你们的码数和米数只是一个例子,我也会用它作为一个例子)

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

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


(当然,类型的倍增是不同的。)

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

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

(我很感激你们的码数和米数只是一个例子,我也会用它作为一个例子)

如果我在写处理长度的代码,(大部分)逻辑将是相同的,无论单位是什么。虽然我可以将包含该逻辑的函数作为一个模板,以便它可以采用不同的单元,但仍然存在
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>>;