C ++中类型的单位

时间:2018-08-21 07:14:07

标签: c++ cpp-core-guidelines

C++ Core Guidlines P.1 change_speed示例中,它显示了一种Speed类型,其用法如下所示:

change_speed(Speed s); // better: the meaning of s is specified
// ...
change_speed(2.3); // error: no unit
change_speed(23m / 10s); // meters per second

我对该示例的最后两行特别感兴趣。第一个似乎表明,如果不为单位提供change_speed的参数,则会抛出错误。最后一行显示使用一些ms文字定义的单位。这两个新功能都是现代C ++版本中的功能吗?如果是这样,将如何实现这种方式,以及需要什么版本的C ++?

2 个答案:

答案 0 :(得分:13)

如评论中所述,核心准则中的示例使用用户定义的文字来构造可直观表示物理量的特定于应用程序的类型。为了说明特定示例,请考虑以下类型:

if (ZipFolder != null)
{
    // Application now has read/write access to all contents in the picked folder (including other sub-folder contents)
    StorageApplicationPermissions.FutureAccessList.AddOrReplace("PickedFolderToken", ZipFolder);
    await Task.Run(() =>
    {
        try
        {
            ZipFile.CreateFromDirectory(ApplicationData.Current.LocalFolder.Path, $"{ZipFolder.Path}\\{Guid.NewGuid()}.zip");
            Debug.WriteLine("folder zipped");
        }
        catch (Exception w)
        {
            Debug.WriteLine(w);
        }
    });

}

跟踪/* "Strong" speed type, unit is always [m/s]. */ struct Speed { long double value; }; /* "Strong" length type, parameterized by a unit as multiples of [m]. */ template <class Period = std::ratio<1>> struct Length { unsigned long long value; }; 对象的单位可能没有多大意义,但对于Length实例而言则没有意义,但让我们在这里考虑最简单的示例。现在,让我们看两个用户定义的文字:

Speed

它们使您可以实例化#include <ratio> auto operator ""_m(unsigned long long n) { return Length<>{n}; } auto operator ""_km(unsigned long long n) { return Length<std::kilo>{n}; } 对象,如下所示:

Length

为了构造一个/* We use auto here, because the suffix is so crystal clear: */ const auto lengthInMeter = 23_m; const auto lengthInKilometer = 23_km; 实例,让我们定义一个适当的运算符,将Speed除以Length

duration

现在,让我们再次看一下核心准则中的示例,

#include <chrono>

template <class LengthRatio, class Rep, class DurationRatio>
auto operator / (const Length<LengthRatio>& lhs,
      const std::chrono::duration<Rep, DurationRatio>& rhs)
{
   const auto lengthFactor = static_cast<double>(LengthRatio::num)/LengthRatio::den;
   const auto rhsInSeconds = std::chrono::duration_cast<std::chrono::seconds>(rhs);

   return Speed{lengthFactor*lhs.value/rhsInSeconds.count()};
}

但最重要的是,如何调用这样的函数:

void change_speed(const Speed& s)
{
    /* Complicated stuff... */
}

正如注释中提到的@KillzoneKid一样,C ++ 11才能正常工作。

答案 1 :(得分:3)

您的代码涉及两件事:

  1. 使用强/单位类型使代码更健壮,即,您可以区分两种整数类型。这是某些语言(例如Ada)内置的,但不是C ++内置的,但是您可以创建包装整数类型的类来模仿这种行为(请参见下文)。

  2. 使用运算符文字以用户友好的方式创建此类的实例,即,您编写的是1s而不是seconds{1}。这只是一项便利功能,在某些地方可能会有用。

使用强整数类型非常有用,因为它使您的代码更容易出错 *

  • 您无法像现实生活中那样在表示持续时间和长度的类型之间进行转换。
  • 在表示相同事物的类型(例如secondshours)中,如果您失去精度,则不会进行隐式转换,例如,无法将seconds转换为{{ 1}},除非您使用浮点类型(hours / float)来表示我们。
  • (隐式和非隐式)转换将为您进行缩放:您可以将double转换为hours,而不必手动乘以3600。
  • 您可以提供现实的运算符,它将为您解决转换问题,例如,在您的示例中,长度和持续时间之间存在除法运算符,从而给出速度。准确的速度类型是根据长度的类型和持续时间的类型自动得出的:
seconds
  • 单元类型是自记录的:如果函数返回auto speed = 70km / 1h; // Don't bother deducing the type of speed, let the compiler do it for you. ,您知道这是什么,您不必希望记录该函数的人返回{{1} }提到这代表微秒...

* 我在这里仅谈论隐式转换,当然,您可以显式进行转换,例如,使用microseconds(精度损失)。


单位类型

“单元”类中的包装整数类型始终可用,但是C ++ 11带来了一种标准的包装整数类型:std::chrono::duration

可以通过以下方式定义“单元”类:

  • 它代表的事物类型:时间,长度,重量,速度,...
  • 用于表示这些数据的C ++类型:unsigned long longduration_cast,...
  • 此类型与同一单元的“ 1类型”之间的比率。

当前,标准仅提供类似持续时间的类型,但是已经进行了讨论(也许是一项提案)来提供更通用的基本单位类型,例如:

int

...其中double是占位符,指示所表示的事物的种类,例如:

template <class Unit, class Rep, class Ratio = std::ratio<1>> class unit;

但这还不是标准的,所以让我们看一下Unit

struct length_t { };

template <class Rep, class Ratio = std::ratio<1>>
using length = unit<length_t, Rep, Ratio>;

std::chrono::duration模板参数是C ++类型:

  • 标准定义的持续时间类型具有整数表示形式(定义为实现)。
  • 基础类型定义可以隐式进行的转换类型:
    • 您可以将整数小时隐式转换为整数秒(乘以3600)。
    • 您可以将整数秒隐式转换为整数小时,因为这样会降低精度。
    • 您可以将整数秒转换为template <class Rep, class Period = std::ratio<1>> class duration; 小时。

Rep模板参数定义了double类型与一秒(这是选择的基本持续时间)之间的比率:

  • std::ratio是一种非常方便的标准定义类型,它简单地表示两个整数之间的比率,并具有相应的运算符(Periodduration,...)。
  • 标准程序提供多种持续时间类型,并具有不同的比率:*/,...

运算符文字

这些已在C ++ 11中引入,并且为literals operators

std::chrono::seconds是标准的,并包含在chrono标准库中:

std::chrono::minutes

s不是标准的,因此应该以{{1​​}}为前缀(因为它是用户定义的),例如using namespace std::chrono_literals; auto one_second = 1s; auto one_hour = 1h; 。您可以这样定义自己的运算符:

m