将C ++转换运算符转换为chrono :: duration-适用于c ++ 17,但不适用于C ++ 14或更低版本
以下代码使用设置了C ++ 17的gcc 7.1.0进行编译,但不能使用设置了C ++ 14的gcc 7.1.0(或Visual Studio 2017)进行编译。在 Wandbox 上很容易复制。
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.
#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;
}
让我们从为什么不这样做开始在C ++ 14中不起作用。 std :: chrono有两个相关的参考: :duration
(其中 std :: chrono :: milliseconds
的别名是):
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 );
模板化的匹配类型更适合 Convert
。但是,如果 Rep2
(又名 Convert
)隐式转换为 std :: chrono :: duration 。对于毫秒
,在Wandbox上的时间为长
。您的 int64_t
转换运算符使隐式转换成为可能。
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.
但这是要抓住的地方。此隐式转换的检查未考虑转换成员函数的简历限定符。这样就选择了重载,但是它被const引用接受。而且您的用户定义的转换运算符不符合 const
的条件! @Galik 在您的帖子评论中指出了这一点。因此,转换在毫秒
的c'tor内失败。
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:
-
标记转换运算符
const
。但是,这将选择转换为m
和i $ c $的
int64_t
c>。
Mark the conversion operator
const
. That will however pick the conversion toint64_t
for bothm
andi
.
将转换为 int64_t
标记为显式
。现在,模板化的重载将不再参与 m
的重载解析。
Mark the conversion to int64_t
as explicit
. Now the templated overload won't participate in overload resolution for m
.
最后,为什么这在C ++ 17中起作用?那将保证复制省略。由于您的转换
可以转换为 std :: chrono ::毫秒 c,因此可用于初始化
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.