https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94679
Jonathan Wakely <redi at gcc dot gnu.org> changed: What |Removed |Added ---------------------------------------------------------------------------- Component|libstdc++ |c++ Ever confirmed|0 |1 Keywords| |link-failure Last reconfirmed| |2020-04-21 Status|UNCONFIRMED |NEW --- Comment #1 from Jonathan Wakely <redi at gcc dot gnu.org> --- That function is intentionally not defined, and only used in unevaluated contexts. It seems to come from the sortable constraint on next_permutation, because commenting out the requires-clause fixes the link failure: template<bidirectional_range _Range, typename _Comp = ranges::less, typename _Proj = identity> requires sortable<iterator_t<_Range>, _Comp, _Proj> The sortable concept uses projected as a template argument to another concept: template<typename _Iter, typename _Rel = ranges::less, typename _Proj = identity> concept sortable = permutable<_Iter> && indirect_strict_weak_order<_Rel, projected<_Iter, _Proj>>; That ends up being used with this constrained alias template: template<__detail::__dereferenceable _Tp> requires requires(_Tp& __t) { { ranges::iter_move(__t) } -> __detail::__can_reference; } using iter_rvalue_reference_t = decltype(ranges::iter_move(std::declval<_Tp&>())); Which uses: namespace ranges { namespace __cust_imove { void iter_move(); template<typename _Tp> concept __adl_imove = (std::__detail::__class_or_enum<remove_reference_t<_Tp>>) && requires(_Tp&& __t) { iter_move(static_cast<_Tp&&>(__t)); }; struct _IMove { // [...] template<typename _Tp> requires __adl_imove<_Tp> || requires(_Tp& __e) { *__e; } constexpr decltype(auto) operator()(_Tp&& __e) const // noexcept([...]) { if constexpr (__adl_imove<_Tp>) return iter_move(static_cast<_Tp&&>(__e)); else if constexpr (is_reference_v<iter_reference_t<_Tp>>) return std::move(*__e); else return *__e; } }; } // namespace __cust_imove inline namespace __cust { inline constexpr __cust_imove::_IMove iter_move{}; } // inline namespace __cust } // namespace ranges So it appears that determining the return type of that function in an unevaluated context causes its instantiation to be kept, leaving a reference to the undefined projected::operator*() function. We might be able to avoid using return type deduction for _IMove::operator() but I wonder if this is really a compiler bug. I imagine this same problem could occur for other uses of return type deduction.