首页 > 解决方案 > 原始类型的强别名

问题描述

我正在阅读这个https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#S-philosophy并且在 P1 中有这个

change_speed(double s);   // bad: what does s signify?
// ...
change_speed(2.3);

A better approach is to be explicit about the meaning of the double (new speed or delta on old speed?) and the unit used:

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

这让我想知道(因为我非常喜欢这个想法)如何实现“速度”类型。

我试过这个

using Speed = double;
void change_speed(Speed s)
{
    cout << s;
}

但这还不够强大,因为我仍然可以做到

double j = 43.0;
change_speed(j);

change_speed(42.0);

注意:如果速度类型可以按照我的意愿工作,那么最后一个仍然需要,但必须是一个演员:-

change_speed(static_cast<Speed>(42.0));

因此,我尝试为它创建一个单字段结构(稍后将被模板化)。但我意识到我需要每一个运算符重载(可能还有一些甚至不存在的运算符),这似乎太多了。

那么,有没有办法做到这一点?

标签: c++

解决方案


您必须使用所需的转换行为和运算符创建一个新类。但是,有一些库可以帮助简化该过程。例如,键入安全

对于整数类型,您还可以在 C++17 中使用空枚举。例如,

enum class Strong : int {};

void func(Strong);

int main() {
    // func(15); doesnt compile
    func(Strong{15});
    func(static_cast<Strong>(15));
    auto s = Strong{15};
    int x = static_cast<int>(s);
}


编辑:

详细地说,可以在强类型中设计不同的功能,因此解决方案最终取决于您想要的确切行为。如果您正在寻找创建强类型的快捷方式或构建块,type_safe 是我见过的最好的东西,也许您可​​以查看实现的想法。

我将在一个示例案例中分享我的想法,我们希望创建一个Strong类似于int.

一个关键的选择是构造,或从基础类型转换。基本上,选择是在显式或隐式构造函数之间,即explicit Strong(int)Strong(int)。如果您想要change_speed(42.0);构建类似的东西,请使用隐式,如果您想要求change_speed(static_cast<Speed>(42.0));显式。

然后,您可以进一步指定要如何转换回基础类型。如果您想要求使用静态转换(例如explicit operator int()),则可能是显式转换运算符,或者如果您可以使用隐式转换(例如),则可能是隐式转换运算符operator int()。也许您根本不想使用转换运算符,而您宁愿提供一个免费的函数来访问底层类型(例如int underlying(Strong const&))。如果您希望能够编写,auto x = Strong{}; int y = x;那么您需要一个隐式转换运算符。如果不是,请使用显式运算符或自由函数。

对于运算符,您当然可以选择将哪些添加到强类型中,对于二元运算符,您可以选择是否提供不对称运算符(例如,可以Strongint被添加在一起吗?)。当然,这个选择会与您之前的选择相互作用。如果您允许从底层类型进行隐式转换,那么对称运算符将已经支持非对称操作。例如Strong operator+(Strong, Strong),如果一个参数int只要int您的定义中有一个隐式构造函数,就会编译Strong

简而言之,它确实取决于你想要什么。但希望这能让您更好地了解一些可用的选择及其产生的影响。

如果我们谈论的是完整的单位系统(米、公斤等),还有更多要讨论的内容。为此,您可以查看Boost.Units的想法。


推荐阅读