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

时间:2018-12-01 20:48:20

标签: c++ c++11 alias chrono

我正在寻找std::chrono期间的强类型解决方案。我的持续时间类型取决于仅运行时值。我使用类似于工厂的类使用运行时值在持续时间之间进行转换。例如:

#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上可用。

请注意,以秒为单位的转换会应用该比率,这与blee的转换不同。由于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++的类似帖子,但我不理解,而且api确实很丑陋且具有侵入性。

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

2 个答案:

答案 0 :(得分:4)

我会再次建议:使用继承

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

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

借助此方法,您可以轻松地创建有时称为“强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通话。

但是说实话,要获得不依赖于黑客的解决方案似乎要付出很小的代价!

(实际上,您实际上只需要将其应用于blee,但出于对称起见,我都将其应用了。这比将tick保留为简单类型更好还是更坏别名取决于读者。)

实际上,我什至可以说这是唯一在语义上“正确”的解决方案,因为您实际上是在创建语义上新的类型,而这并不是别名。别名是为现有类型创建一个方便的替代名称。

答案 1 :(得分: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