The following code compiles with gcc 7.1.0 with C++17 set but does not compile with C++14 set (or Visual Studio 2017). It is easy to reproduce on Wandbox.
What has to be done to make it work with C++11/14?
#include <iostream>
#include <chrono>
int main()
{
struct Convert
{
operator std::chrono::milliseconds()
{
std::cout << "operator std::chrono::milliseconds" << std::endl;
return std::chrono::milliseconds(10);
}
operator int64_t ()
{
std::cout << "operator int64_t" << std::endl;
return 5;
}
};
Convert convert;
std::chrono::milliseconds m(convert);
std::cout << m.count() << std::endl;
int64_t i(convert);
std::cout << i << std::endl;
}
Class template std::chrono::duration represents a time interval. It consists of a count of ticks of type Rep and a tick period, where the tick period is a compile-time rational fraction representing the time in seconds from one tick to the next. The only data stored in a duration is a tick count of type Rep .
Conversion Operators in C++ C++ supports object oriented design. So we can create classes of some real world objects as concrete types. Sometimes we need to convert some concrete type objects to some other type objects or some primitive datatypes. To make this conversion we can use conversion operator.
Chrono in C++ chrono is the name of a header and also of a sub-namespace: All the elements in this header (except for the common_type specializations) are not defined directly under the std namespace (like most of the standard library) but under the std::chrono namespace.
Let's start with why this doesn't work in C++14. There are two relevant c'tors for std::chrono::duration
(which std::chrono::milliseconds
is aliased to be):
duration( const duration& ) = default;
template< class Rep2 >
constexpr explicit duration( const Rep2& r );
The templated one is a much better match for an argument of type Convert
. But it will only participate in overload resolution if Rep2
(a.k.a Convert
) is implicitly convertible to the representation type of std::chrono::duration
. For milliseconds
, that is long
on Wandbox. Your int64_t
conversion operator makes that implicit conversion possible.
But here's the catch. The check for this implicit conversion doesn't take the cv-qualifiers of the conversion member function into account. So that overload is chosen, but it accepts by a const reference. And your user defined conversion operator is not const
qualified! @Galik noted it in the comments to your post. As such, the conversion fails inside the c'tor of milliseconds
.
So how to resolve it? Two ways:
Mark the conversion operator const
. That will however pick the conversion to int64_t
for both m
and i
.
Mark the conversion to int64_t
as explicit
. Now the templated overload won't participate in overload resolution for m
.
And finally, why does this work in C++17? That would be guaranteed copy elision. Since your Convert
has a conversion to std::chrono::milliseconds
, it is used to initialized m
directly. The minutia of it involves not even needing to choose the copy constructor, just to elide it later.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With