将C ++转换运算符转换为chrono :: duration-适用于c ++ 17,但不适用于C ++ 14或更低版本

将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:


  1. 标记转换运算符 const 。但是,这将选择转换为 m i $ c $的 int64_t c>。

  1. Mark the conversion operator const. That will however pick the conversion to int64_t for both m and i.

将转换为 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.