Search code examples
c++c++-chrononoexcept

Implicitly deleted special functions with std::chrono::time_point


I have some code like this

#include <chrono>
using std::chrono::steady_clock;
using std::chrono::time_point;

class MyClass
{
public:
    MyClass() noexcept = default;
    MyClass(MyClass const&) noexcept = default;
    MyClass(MyClass&&) noexcept = default;
    MyClass& operator=(MyClass const&) noexcept = default;
    MyClass& operator=(MyClass&&) noexcept = default;
private:
    time_point<steady_clock> timestamp; 
};

int main() {
    MyClass myObject;
    MyClass myOtherObject(myObject);
    return 0;
}

Trying to compile it results in

prog.cpp: In function ‘int main()’:
prog.cpp:18:10: error: use of deleted function ‘constexpr MyClass::MyClass()’
  MyClass myObject;
          ^~~~~~~~
prog.cpp:8:5: note: ‘constexpr MyClass::MyClass() noexcept’ is implicitly deleted because
its exception-specification does not match the implicit exception-specification ‘’
     MyClass() noexcept = default;

But compiles fine if I remove the noexcept specifier for the default constructor:

MyClass() = default;
MyClass(MyClass const&) noexcept = default;
MyClass(MyClass&&) noexcept = default;
MyClass& operator=(MyClass const&) noexcept = default;
MyClass& operator=(MyClass&&) noexcept = default;

So I have 2 questions:

  • How come time_point's default constructor isn't noexcept? I mean, as far as I understand it only contains an integer of some type representing the time since epoch, which is supposed to be 0 for the default constructor if I believe cppreference
  • Why does removing noexcept from only the default constructor work? If the compiler says that it had generated MyClass() and not MyClass() noexcept and therefore deleted it, it should be the same for MyClass(MyClass const&), right? But the compiler lets me do the copy without complaining here...

Solution

    • How come time_point's default constructor isn't noexcept?

    When noexcept first went into the standard in C++11 the committee was very hesitant to "over apply" it. noexcept is much easier to add to the standard than to remove. In applying it conservatively, "conditional" noexcept was often shunned.

    I would like to see "conditional" noexcept applied to <chrono>. For the time_point default constructor the noexcept would be conditional on if rep is noexcept default constructible. rep doesn't have to be an integer. It might be a class type with a throwing default constructor. But the specialization steady_clock::time_point would always be noexcept because it's rep is required to be integral.

    • Why does removing noexcept from only the default constructor work?

    Because time_point has a user-declared default constructor but a compiler-supplied copy constructor. This is one example of how compiler-supplied special members can be so much "smarter" than user-declared ones.

    The compiler-supplied copy constructor of time_point effectively already has a "conditional" noexcept. You get this for free by writing less code. Had the time_point default constructor been compiler-supplied (which it should have been), then it would work in your example too.