带有std::chrono::duration的用户定义重载操作符*

User-defined overloaded operator * with std::chrono::duration

本文关键字:定义 重载 操作符 用户 std chrono duration 带有      更新时间:2023-10-16

我创建了一个Frequency类模板,打算与std::chrono::duration一起工作。Frequency对象每单位持续时间存储许多周期(两者都使用其类型的模板参数)。其思想是将Frequency乘以持续时间产生类型为rep的对象。下面是类定义。除了相关的成员外,我都省略了。

#include <ratio>
#include <chrono>
using namespace std::chrono;
template <typename Rep, typename Period = std::ratio<1>>
class Frequency
{
public:
    explicit Frequency(Rep cycles) : _cycles{cycles} { }
    friend Rep operator *(const Frequency<Rep, Period>& f, const duration<Rep, Period> d)
    {
        return f._cycles * d.count();
    }
    friend Rep operator *(const duration<Rep, Period> d, const Frequency<Rep, Period>& f)
    {
        return f._cycles * d.count();
    }
private:
    Rep _cycles; ///> The number of cycles per period.
};

问题(我认为)是std::chrono::duration也重载了*操作符,如下所示:

template< class Rep1, class Period, class Rep2 >
duration<typename std::common_type<Rep1,Rep2>::type, Period>
    constexpr operator*( const duration<Rep1,Period>& d,
                         const Rep2& s );
template< class Rep1, class Rep2, class Period >
duration<typename std::common_type<Rep1,Rep2>::type, Period>
    constexpr operator*( const Rep1& s,
                         const duration<Rep2,Period>& d );

根据cppreference,这些方法

将持续时间d转换为Rep1和Rep2之间的通用类型,并将转换后的节拍数乘以s。

我相信编译器正在尝试使用这些重载的操作符模板,而不是我的重载。当我编写以下代码时:

Frequency<double> f{60.0};
duration<double> d{1.0};
double foo = f * d;

我在VS2013中得到类似以下错误的东西:

C:Program Files (x86)Microsoft Visual Studio 12.0VCINCLUDEtype_traits(1446) : error C2446: ':' : no conversion from 'double' to 'Frequency<double,std::ratio<0x01,0x01>>'
        Constructor for class 'Frequency<double,std::ratio<0x01,0x01>>' is declared 'explicit'
        source_file.cpp(27) : see reference to class template instantiation 'std::common_type<Frequency<double,std::ratio<0x01,0x01>>,Rep>' being compiled
        with
        [
            Rep=double
        ]

第1446行是std::common_type的VS实现。

那么,是否有一种方法可以确保调用我的重载而不是调用std::chrono::duration中的重载?我知道我可以通过不使用操作符重载和编写一个不同命名的方法来解决这个问题,但是如果可能的话,我想更好地理解这里发生了什么。(加上重载更方便)

另外,如果这个问题是重复的,我很抱歉,但我在搜索后无法找到任何类似的内容。

在此代码中:

Frequency<double> f{60.0};
duration<double> d{1.0};
double foo = f * d;

编译器说:"哦,我必须找到一个operator*,它包含一个Frequency<double>和一个std::chrono::duration<double>operator*有哪些可能性?然后生成一个可能的列表,主要由以下两部分组成:

friend Rep operator *(const Frequency<Rep, Period>& f, const duration<Rep, Period> d)
{
    return f._cycles * d.count();
}
template< class Rep1, class Rep2, class Period >
duration<typename std::common_type<Rep1,Rep2>::type, Period>
constexpr operator*( const Rep1& s,
                     const duration<Rep2,Period>& d );
然后,它试图找出确切的每个签名是什么。第一个是:
Rep operator *(const Frequency<Rep, Period>& f, const duration<Rep, Period> d)

并尝试第二个结果:

error C2446: ':' : no conversion from 'double' to 'Frequency<double,std::ratio<0x01,0x01>>'
        Constructor for class 'Frequency<double,std::ratio<0x01,0x01>>' is declared 'explicit'
        source_file.cpp(27) : see reference to class template instantiation 'std::common_type<Frequency<double,std::ratio<0x01,0x01>>,Rep>' being compiled

因为它甚至不能弄清楚签名是什么,它不知道选择哪一个,它放弃了。在这种情况下,我认为这是MSVC实现operator*的错误。

显而易见的解决方法是使common_type<Frequency<double>,Rep>能够编译,可能是通过消除构造函数上的explicit限制。aschepler观察到,将common_type:

专门化可能更聪明、更有用。
namespace std {
    template <typename Rep, typename Period>
    struct common_type<Frequency<Rep,Period>, std::chrono::duration<Rep,Period>>   
    {
        typedef Rep type;
    };
    template <typename Rep, typename Period>
    struct common_type<std::chrono::duration<Rep,Period>, Frequency<Rep,Period>>   
    {
        typedef Rep type;
    };
}