How can std::chrono::duration::duration() be constexpr?

后端 未结 1 1157
借酒劲吻你
借酒劲吻你 2021-02-05 10:19

The default constructor of std::chrono::duration is defined as follows:

constexpr duration() = default;

(For example, see cppreference.

相关标签:
1条回答
  • 2021-02-05 10:44

    7.1.5 The constexpr specifier [dcl.constexpr] says:

    The definition of a constexpr constructor shall satisfy the following requirements:

    • the class shall not have any virtual base classes;
    • for a defaulted copy/move constructor, the class shall not have a mutable subobject that is a variant member;
    • each of the parameter types shall be a literal type;
    • its function-body shall not be a function-try-block;

    In addition, either its function-body shall be = delete, or it shall satisfy the following requirements:

    • either its function-body shall be = default, or the compound-statement of its function-body shall satisfy the requirements for a function-body of a constexpr function;
    • every non-variant non-static data member and base class sub-object shall be initialized (12.6.2);
    • if the class is a union having variant members (9.5), exactly one of them shall be initialized;
    • if the class is a union-like class, but is not a union, for each of its anonymous union members having variant members, exactly one of them shall be initialized;
    • for a non-delegating constructor, every constructor selected to initialize non-static data members and base class sub-objects shall be a constexpr constructor;
    • for a delegating constructor, the target constructor shall be a constexpr constructor.

    In a nutshell, = default is a valid definition of a constexpr default constructor as long as the other requirements above are met.

    So how does this work with uninitialized constructions?

    It doesn't.

    For example:

    constexpr seconds x1{};
    

    The above works and initializes x1 to 0s. However:

    constexpr seconds x2;
    
    error: default initialization of an object of const type 'const seconds'
           (aka 'const duration<long long>') without a user-provided default
            constructor
        constexpr seconds x2;
                          ^
                            {}
    1 error generated.
    

    So to create a constexpr default constructed duration, you must zero-initialize it. And the = default implementation allows one to zero-initialize with the {}.

    Complete working demo:

    template <class Rep>
    class my_duration
    {
        Rep rep_;
    public:
        constexpr my_duration() = default;
    };
    
    
    int
    main()
    {
        constexpr my_duration<int> x{};
    }
    

    Interesting Sidebar

    I learned something in writing this answer, and wanted to share:

    I kept wondering why the following doesn't work:

    using Rep = int;
    
    class my_duration
    {
        Rep rep_;
    public:
        constexpr my_duration() = default;
    };
    
    
    int
    main()
    {
        constexpr my_duration x{};
    }
    
    error: defaulted definition of default constructor is not constexpr
            constexpr my_duration() = default;
            ^
    

    Why does making this class a non-template break the constexpr default constructor?!

    Then I tried this:

    using Rep = int;
    
    class my_duration
    {
        Rep rep_;
    public:
        my_duration() = default;  // removed constexpr
    };
    
    
    int
    main()
    {
        constexpr my_duration x{};
    }
    

    And the compilers like it again.

    If there isn't already a CWG issue on this, there probably should be. The behavior seems a bit inconsistent. And this is probably just because we (the entire industry) are still learning about constexpr.

    0 讨论(0)
提交回复
热议问题