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
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':
/opt/wandbox/gcc-head/include/c++/14.0.0/bits/stl_pair.h:307:57: required by
substitution of 'template pair(const _T1&, const _T2&)->
std::pair<_T1, _T2> requires (std::pair<_T1, _T2>::_S_constructible)() [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::second' invalidly declared function
type
194 | _T2 second;///< The second member
| ^~
Note that std::pair has a "decaying" deduction guide
template
pair(T1, T2) -> pair;
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
constexpr bool is_copy_constructible_v = true;
template
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
#endif
)
: first(t1)
{}
};
template
p(T1) -> p;
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':
prog.cc:19:31: required by substitution of 'template p(const T1&)->
p requires (p::do_is_copy_constructible)() [with T1 = void(int&)]'
prog.cc:38:23: required from here
prog.cc:9:6: error: data member 'p::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 f