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

ぃ、小莉子 提交于 2021-02-06 08:45:55

问题


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

constexpr duration() = default;

(For example, see cppreference.com or the libstdc++ source.)

However, cppreference.com also says this about constexpr constructors:

A constexpr constructor must satisfy the following requirements:

...

every base class and every non-static member must be initialized, either in the constructors initialization list or by a member brace-or-equal initializer. In addition, every constructor involved must be a constexpr constructor and every clause of every brace-or-equal initializer must be a constant expression

And in case I was confused about default constructors, cppreference.com seems to say that default constructors brought into being with = default aren't defined differently than implicit default constructors.

Yet, the rep type for (most) durations is a bare integer type. So, shouldn't the explicit = default default constructor for duration be equivalent to

constexpr duration() {}

which of course would leave the integer member variable of type duration::rep uninitialized? And, in fact, isn't the standard behaviour of duration such that default-constructed values are uninitialized? (But I can't find a reference that explicitly says this.)

So how can the = default constructor for duration be constexpr if it leaves a non-static member variable uninitialized? What am I missing?


回答1:


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.



来源:https://stackoverflow.com/questions/36342296/how-can-stdchronodurationduration-be-constexpr

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!