https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94033
--- Comment #11 from Jonathan Wakely <redi at gcc dot gnu.org> --- I think it doesn't show up for gcc-9 because there are also std::tuple changes on master that make the bug show up. And I think what's happening is another instance of PR 41437. When std::optional<mutation> is instantiated it causes std::is_constructible<mutation> to be instantiated, but gives the wrong answer because of PR 41437. Later when the value of std::is_nothrow_default_constructible<mutation> is needed, the bad instantiation of is_constructible<mutation> is reused, which means that the first condition in the __and_ here is true (but shouldn't be) and so we try the second condition, which is ill-formed: /// is_nothrow_default_constructible template<typename _Tp> struct is_nothrow_default_constructible : public __and_<is_default_constructible<_Tp>, __is_nt_default_constructible_impl<_Tp>> { }; /home/jwakely/gcc/10/include/c++/10.0.1/type_traits:966:47: error: 'mutation::mutation()' is private within this context 966 | : public integral_constant<bool, noexcept(_Tp())> | ^~~~~ We should never try to construct _Tp() when it isn't default constructible. The cached bad value of is_constructible<mutation> makes us try to do something impossible. Lending weight to this theory is the fact that explicitly checking is_constructible_v<mutation> *before* instantiating optional<mutation> makes the bug go away: #include <optional> #include <tuple> template <bool B> struct abc {}; template <typename T> struct future : public abc<std::is_trivially_constructible<std::tuple<T>>::value> {}; class mutation { mutation(); friend class std::optional<mutation>; }; #ifdef FIX static_assert( !std::is_nothrow_default_constructible<mutation>::value ); #endif using mutation_opt = std::optional<mutation>; future<mutation_opt> foo(); template <typename Consumer> future<mutation_opt> consume_partitions() { return foo(); } future<mutation_opt> bar() { return consume_partitions<int>(); } future<mutation> zed(); future<mutation> apply_counter_update() { return zed(); } Define FIX to make the bug vanish.