The following code must be accepted (as it does in Clang and MSVC): struct A { A( float ) {} template<class U> explicit A( U ) {} }; template<class T> concept CopyFromIntList = requires( T t ) { t = { 1 }; }; static_assert( !CopyFromIntList<A> ); because 't = {1}' is invalid: chosen constructor is explicit in copy-initialization. But in GCC static_assert fails. Online demo: https://gcc.godbolt.org/z/Pf47E6dno
Reduced testcase without the constraint that GCC accepts: struct A { A( float ) {} template<class U> explicit A( U ) {} }; void f(A t) { t = {1}; }
Oy, started with r9-3735-gb5ff4f5c0d61e5: commit b5ff4f5c0d61e52e27a0727ae9e011aab525ccfd Author: Marek Polacek <polacek@redhat.com> Date: Tue Oct 30 19:59:41 2018 +0000 Implement P0892R2, explicit(bool).
This hunk broke it: @@ -3251,6 +3251,12 @@ add_template_candidate_real (struct z_candidate **candidates, tree tmpl, goto fail; } + /* Now the explicit specifier might have been deduced; check if this + declaration is explicit. If it is and we're ignoring non-converting + constructors, don't add this function to the set of candidates. */ + if ((flags & LOOKUP_ONLYCONVERTING) && DECL_NONCONVERTING_P (fn)) + return NULL; It doesn't check for BRACE_ENCLOSED_INITIALIZER_P (or some similar flag meaning we're initializing from an initializer_list) and that's the bug.
The trunk branch has been updated by Marek Polacek <mpolacek@gcc.gnu.org>: https://gcc.gnu.org/g:a226590fefb35ed66adf73d85cefe49048a78ab8 commit r13-6765-ga226590fefb35ed66adf73d85cefe49048a78ab8 Author: Marek Polacek <polacek@redhat.com> Date: Fri Mar 17 18:25:13 2023 -0400 c++: explicit ctor and list-initialization [PR109159] When I implemented explicit(bool) in r9-3735, I added this code to add_template_candidate_real: + /* Now the explicit specifier might have been deduced; check if this + declaration is explicit. If it is and we're ignoring non-converting + constructors, don't add this function to the set of candidates. */ + if ((flags & LOOKUP_ONLYCONVERTING) && DECL_NONCONVERTING_P (fn)) + return NULL; but as this test demonstrates, that's incorrect when we're initializing from a {}: for list-initialization we consider explicit constructors and complain if one is chosen. PR c++/109159 gcc/cp/ChangeLog: * call.cc (add_template_candidate_real): Add explicit decls to the set of candidates when the initializer is a braced-init-list. libstdc++-v3/ChangeLog: * testsuite/20_util/pair/cons/explicit_construct.cc: Adjust dg-error. * testsuite/20_util/tuple/cons/explicit_construct.cc: Likewise. * testsuite/23_containers/span/explicit.cc: Likewise. gcc/testsuite/ChangeLog: * g++.dg/cpp0x/explicit16.C: New test.
Fixed on trunk so far.
The patch caused non-bug bug 109247, so I don't think it's appropriate to backport this (?).