Consider the following code:
#include <type_traits>
template<typename T>
struct A1 {
T t;
// implicitly-declared default constructor
};
template<typename T>
struct A2 {
T t;
// explicitly-declared default constructor without noexcept
A2() = default;
};
template<typename T>
struct A3 {
T t;
// explicitly-declared default constructor with noexcept
A3() noexcept(std::is_nothrow_default_constructible<T>::value) = default;
};
Are these three default constructors equivalent in C++?
None of these is equivalent to each other.
First, A1<T>
is an aggregate whereas A2<T>
and A3<T>
are not. See godbolt. The definition of aggregate has varied across standards, and this is an aggregate (again) in C++20... so if you support older standards, A1<T>
will have very different powers in each.
This has a strong effect on where and how these can be constructed. If one can construct a T
, one can construct an A1<T>
but not necessarily an A2<T>
or A3<T>
. See godbolt.
So A1<T>
varies from the others in whether you can construct it at all.
A2<T>
and A3<T>
differ in whether the default constructor is noexcept
. std::is_nothrow_default_constructible_v
does not check only if a value is nothrow constructible but also destructible. Whereas the defaulted declaration without noexcept specification only checks constructors. See godbolt. (This is LWG 2116.)