https://gcc.gnu.org/bugzilla/show_bug.cgi?id=119848
Bug ID: 119848 Summary: Concept check makes it impossible to return types that depend on the type being defined Product: gcc Version: 14.2.1 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c++ Assignee: unassigned at gcc dot gnu.org Reporter: jpakkane at gmail dot com Target Milestone: --- This might not be a bug in GCC itself, but in the C++ specification. The behaviour is the same for Clang and MSVC. Still, this seems like a thing that a developer might reasonably expect to work. ## Description Assume that we have a non-templated string type called MyString and a template container class MyVector<T>. The string class provides a split function. Thus the relevant pieces of code would be like this: class MyString { ... MyVector<MyString> split() const; }; This works fine. Now let's assume that MyVector has requirements on the types it stores, for example that it can be noexcept-moved. If you specify them as `static_assert`s in the MyVector class everything still works. However if you specify them as a concept, then things fail. Assuming the concept is named `WellBehaved` the output is: <source>:30:5: error: constraints not satisfied for class template 'MyVector' [with T = MyString] 30 | MyVector<MyString> split(); | ^~~~~~~~~~~~~~~ <source>:13:10: note: because 'MyString' does not satisfy 'WellBehaved' 13 | template<WellBehaved T> However if you specify the return value as `auto` like this: auto split() const { /* implementation here */ } things work again. ## Full source code https://gcc.godbolt.org/z/YjxWGdKbr ------- #include<utility> template<typename T> concept WellBehaved = requires(T a, T b, const T &c, T &&d) { requires noexcept(a = b); requires noexcept(a = std::move(b)); requires noexcept(T{}); requires noexcept(T{b}); requires noexcept(T{c}); requires noexcept(T{d}); }; template<WellBehaved T> struct Holder { T foo; }; struct Failing { Failing() noexcept; Failing(Failing &&o) noexcept; Failing(const Failing &o) noexcept ; Failing& operator=(Failing &&o) noexcept; Failing& operator=(const Failing &o) noexcept ; // The following line leads to // a compile error because "Failing" is not // fully defined yet. // // If you comment it out, everything compiles. Holder<Failing> return_my_own_type_wrapped(); // If the return type is deduced, things work. auto return_my_own_type_wrapped_deduced() { return Holder<Failing> { *this }; }; }; Holder<Failing> instantiation;