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?
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 .
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.
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?!
(Update: This now compiles with C++20)
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 Fixed in C++20.constexpr
.
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