问题
I've created a Frequency class template intended to work in conjunction with std::chrono::duration. A Frequency object stores a number of cycles per unit duration (both using template parameters for their types). The idea is that multiplying a Frequency by a duration produces an object of type Rep. Here's the class definition. I've omitted all but the relevant members.
#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.
};
The problem (I think) is that std::chrono::duration also overloads the * operator, as follows:
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 );
According to cppreference, each of these methods
Converts the duration d to one whose rep is the common type between Rep1 and Rep2, and multiplies the number of ticks after conversion by s.
I believe that the compiler is trying to use these overloaded operator templates instead of my overloads. When I write the following code:
Frequency<double> f{60.0};
duration<double> d{1.0};
double foo = f * d;
I get something like the following error in 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
]
And line 1446 is the VS implementation of std::common_type
.
So, is there a way to ensure that my overloads get called instead of those in std::chrono::duration? I know that I could solve the problem by not using operator overloading and writing a differently named method instead, but I'd like to get a better understanding of what's going on here if possible. (Plus the overloads are more convenient.)
Also, I apologize if this question is a duplicate, but I was unable to find anything similar after searching.
回答1:
In this code:
Frequency<double> f{60.0};
duration<double> d{1.0};
double foo = f * d;
The compiler says "oh, I have to find an operator*
that takes a Frequency<double>
and a std::chrono::duration<double>
. What operator*
possibilities are there? And it generates a list of possibilities, composed mostly of these two:
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 );
Then, it tries to figure out exactly what each of the signatures are. The first one is:
Rep operator *(const Frequency<Rep, Period>& f, const duration<Rep, Period> d)
and trying the second one results in:
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
Since it can't even figure out what the signature is, it can't figure out which one to select, and it gives up. I would consider this a bug in MSVC's implementation of operator*
in this case.
The obvious workaround is to make common_type<Frequency<double>,Rep>
able to compile, possibly by removing the explicit
limitation on the constructor. aschepler observes it might be smarter and more useful to instead specialize 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;
};
}
来源:https://stackoverflow.com/questions/24022142/user-defined-overloaded-operator-with-stdchronoduration