我创建了一个频率类模板,旨在与std::chrono::duration一起使用。频率对象存储每单位持续时间的多个周期(两者都使用其类型的模板参数)。这个想法是将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转换为其rep是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.0\VC\INCLUDE\type_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中的重载?我知道我可以通过不使用运算符重载和编写不同命名的方法来解决问题,但是如果可能的话,我想更好地理解这里发生了什么。 (加上过载更方便。)
另外,如果这个问题重复,我很抱歉,但在搜索后我找不到类似的内容。
答案 0 :(得分:3)
在此代码中:
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;
};
}