Thanks to @Yakk and @TC for their explanation. I feel the example should make everything clearer:
struct wrapper { int value; // non-explicit constexpr constructor constexpr wrapper(int v) noexcept : value(v) {} // non-constexpr copy & move constructors wrapper(const wrapper& that) noexcept : value(that.value) {} wrapper(wrapper&& that) noexcept : value(that.value) {} }; constexpr wrapper make_wrapper(int v) { return {v}; } int main() { constexpr auto x = make_wrapper(123); // error! copy/move construction, // but no constexpr copy/move ctor constexpr int y = make_wrapper(123).value; // ok static_assert(y == 123, ""); // passed }
So, make_wrapper successfully returns a constexpr wrapper ; it is copying / moving (although this is usually eliminated by compilers), which prevents compilation of the code, since there is no constexpr copy / move constructor.
We can check the constexpr returned (temporary) wrapper object using its member value to initialize the constexpr variable.
Zizheng tai
source share