区分具有相同类型和比率的 std::chrono 持续时间?

Differentiating std::chrono durations that have the same type and ratio?

我正在寻找 std::chrono 持续时间的强类型解决方案。我有依赖于 运行-time only 值的持续时间类型。我使用类似工厂的 class 在使用 运行 时间值的持续时间之间进行转换。例如:

#include <chrono>
#include <cstdio>

using dseconds = std::chrono::duration<double>;
using blee = std::chrono::duration<double, std::ratio<1,1>>;
using tick = std::chrono::duration<size_t, std::ratio<1,64>>;

struct converter {
    tick to_tick(dseconds s) const {
        return std::chrono::duration_cast<tick>(s / runtime_ratio);
    }

    tick to_tick(blee b) const {
        return std::chrono::duration_cast<tick>(b);
    }

private:
    double runtime_ratio = 0.5;
};

int main(int, char**) {
    converter c;
    printf("%zu", c.to_tick(dseconds{1}).count());
    printf("%zu", c.to_tick(blee{1}).count());
}

示例可在 compiler explorer 上找到。

请注意,从秒转换应用比率,与从出血转换不同。这不会编译,因为 dsecondsblee 实际上是同一类型。

<source>:13:10: error: 'tick converter::to_tick(blee) const' cannot be overloaded with 'tick converter::to_tick(dseconds) const'
     tick to_tick(blee b) const {
          ^~~~~~~

<source>:9:10: note: previous declaration 'tick converter::to_tick(dseconds) const'
     tick to_tick(dseconds s) const {
          ^~~~~~~

我试过使用模板化别名来区分 blee,但这没有用。我试过在其他名称空间或结构中嵌套别名,但也没有用。我读过 fluentc++ 的 post 关于类似的东西,但我不明白,而且 api 真的很丑陋和干扰。

有没有办法以相同的类型和比率强类型化 2 个持续时间别名?

好吧,我找到了一些破解方法。如果您使用 std::ratio<2,2>,它等同于 std::ratio<1,1>,编译器将停止抱怨并将 dsecondsblee 视为不同的类型。

如果有人有更好的解决方案,我会保持开放状态。

#include <chrono>
#include <cstdio>

using dseconds = std::chrono::duration<double>;
using blee = std::chrono::duration<double, std::ratio<2,2>>;
using tick = std::chrono::duration<size_t, std::ratio<1,64>>;

struct converter {
    tick to_tick(dseconds s) const {
        return std::chrono::duration_cast<tick>(s / runtime_ratio);
    }

    tick to_tick(blee b) const {
        return std::chrono::duration_cast<tick>(b);
    }

private:
    double runtime_ratio = 0.5;
};

int main(int, char**) {
    converter c;
    printf("%zu", c.to_tick(dseconds{1}).count());
    printf("%zu", c.to_tick(blee{1}).count());
}

compiler explorer

我再建议一下:使用继承

现在您的问题是通过 using 使用类型别名。这不会创建新类型,只会为旧类型创建新名称。你试图用相同的类型重载两次,这是没有意义的,也是不可能的,因为重载不关心新名称。

但是继承创建类型。那是它的工作;这就是它的目的。

有了它,您可以轻松地创建有时被称为 "strong typedef" 的东西 — 一种新鲜的、独特的类型,在其他方面的行为与您正在派生的东西非常相似。

它确实需要少量体操,但最终结果非常性感,再工作半小时可以微不足道地改善。 (例如,在那个向上转换的 ctor 中使用一点移动语义可能不会造成伤害……)。

我们开始:

#include <chrono>
#include <cstdio>

using dseconds = std::chrono::duration<double>;

struct blee : std::chrono::duration<double, std::ratio<1,1>>
{
    using DurationType = std::chrono::duration<double, std::ratio<1,1>>;
    using DurationType::DurationType;
    blee(const DurationType& other) : DurationType(other) {}
};

struct tick : std::chrono::duration<size_t, std::ratio<1,64>>
{
    using DurationType = std::chrono::duration<size_t, std::ratio<1,64>>;
    using DurationType::DurationType;
    tick(const DurationType& other) : DurationType(other) {}
};

struct converter {
    tick to_tick(dseconds s) const {
        return std::chrono::duration_cast<tick::DurationType>(s / runtime_ratio);
    }

    tick to_tick(blee b) const {
        return std::chrono::duration_cast<tick::DurationType>(b);
    }

private:
    double runtime_ratio = 0.5;
};

int main(int, char**) {
    converter c;
    printf("%zu", c.to_tick(dseconds{1}).count());
    printf("%zu", c.to_tick(blee{1}).count());
}

(live demo)

所以,好的,您必须花几行来设置类型。而且,好吧,你必须稍微扩展 duration_cast 调用。

但老实说,为了获得不依赖 hackery 的解决方案,这似乎是一个很小的代价!

(事实上,你真的只需要将它应用到 blee,但为了对称我对两者都做了。这比离开 tick 好还是坏作为一个简单的类型别名取决于 reader.)

我什至会说这是唯一的语义 "correct" 解决方案,因为您实际上是在创建语义上的新类型,而这不是别名的用途。别名用于为某些现有类型创建一个方便的替代名称。