https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110853
Bug ID: 110853 Summary: [c++-concepts] Bad interaction between deduction guide with decay and constraints Product: gcc Version: 14.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c++ Assignee: unassigned at gcc dot gnu.org Reporter: daniel.kruegler at googlemail dot com Target Milestone: --- The following simplified code snippet demonstrates a breakage our company code when updating gcc from C++17 to C++20. I'm starting first with the original example, because it provides a bit more context information: ------------------------------------ #include <utility> void value_twice(int& result) { result = 2 * result; } auto key_value_gen() { return std::pair("key", value_twice); } int main() { } ------------------------------------ Using gcc HEAD 14.0.0 20230728 and compiled with the previous settings -Wall -Wextra -pedantic -std=c++17 the code is accepted, but switching to -Wall -Wextra -pedantic -std=c++20 it becomes rejected with the following diagnostics: ------------------------------------ In file included from /opt/wandbox/gcc-head/include/c++/14.0.0/utility:69, from prog.cc:1: /opt/wandbox/gcc-head/include/c++/14.0.0/bits/stl_pair.h: In instantiation of 'struct std::pair<char [4], void(int&)>': /opt/wandbox/gcc-head/include/c++/14.0.0/bits/stl_pair.h:307:57: required by substitution of 'template<class _T1, class _T2> pair(const _T1&, const _T2&)-> std::pair<_T1, _T2> requires (std::pair<_T1, _T2>::_S_constructible<const _T1&, const _T2&>)() [with _T1 = char [4]; _T2 = void(int&)]' prog.cc:10:38: required from here /opt/wandbox/gcc-head/include/c++/14.0.0/bits/stl_pair.h:194:11: error: data member 'std::pair<char [4], void(int&)>::second' invalidly declared function type 194 | _T2 second; ///< The second member | ^~~~~~ ------------------------------------ Note that std::pair has a "decaying" deduction guide template<class T1, class T2> pair(T1, T2) -> pair<T1, T2>; but the error message reveals that attempts the produce a std::pair of the undecayed types. Additional information: a) Current MSVC accepts the code but clang also rejects it but for different reasons than gcc (see below). b) I'm aware that a simple workaround exists by returning std::pair("key", &value_twice) instead, and this is what we did to fix this. Nonetheless I think that not everyone is able to fix such a problem in similar code when it was provided by thirdparty libraries. Basically the same error occurs when we use std::tuple(value_twice) instead. The C++20 std::pair implementation uses noexcept, conditional explicit, and trailing requires-clause based on static member functions as predicates, but for gcc the problem can be reduced to the trailing requires-clause alone: -------------------------------------- #define WITH_FUNC 1 template<class T> constexpr bool is_copy_constructible_v = true; template<class T1> struct p { T1 first; static constexpr bool do_is_copy_constructible() { return true; } p(const T1& t1) requires ( #if WITH_FUNC do_is_copy_constructible() // Line 19 #else is_copy_constructible_v<T1> #endif ) : first(t1) {} }; template<class T1> p(T1) -> p<T1>; void value_twice(int& result) { result = 2 * result; } auto value_gen() { return p(value_twice); // line 38 } int main() {} -------------------------------------- If we define WITH_FUNC to 0, the code is accepted, otherwise (as shown above), the code is rejected with: -------------------------------------- prog.cc: In instantiation of 'struct p<void(int&)>': prog.cc:19:31: required by substitution of 'template<class T1> p(const T1&)-> p<T1> requires (p<T1>::do_is_copy_constructible)() [with T1 = void(int&)]' prog.cc:38:23: required from here prog.cc:9:6: error: data member 'p<void(int&)>::first' invalidly declared function type 9 | T1 first; | ^~~~~ -------------------------------------- Note that clang does accept the reduced case, even though it rejects the original example as well due to slightly different reasons, which I will report separately to them. The reduced case uses the same implementation strategy as libstdc++ by means of a static member function. What's special here is that the body of such a function is a complete-class context of p, which I guess causes the error here because the trailing requires-clause is not a complete-class context of p, so one could say that this is actually a libstdc++ defect to use this implementation strategy, it seems odd to me that the compiler attempts to instantiate p with the undecayed function here and would like to open this initially as compiler defect since the corresponding function does not actually depend on p being complete. Feel free to correct me, but in case of a correction of my understanding I would like to change this to a libstdc++ issue instead of closing it because that would mean that libstdc++ cannot use there static member function predicate approach (A free function template could be used instead of a static member function, I tested that).