c++: fix explicit/copy problem [PR109247]

In the testcase, the user wants the assignment to use the operator= declared
in the class, but because [over.match.list] says that explicit constructors
are also considered for list-initialization, as affirmed in CWG1228, we end
up choosing the implicitly-declared copy assignment operator, using the
explicit constructor template for the argument, which is ill-formed.  Other
implementations haven't implemented CWG1228, so we keep getting bug reports.

Discussion in CWG led to the idea for this targeted relaxation: if we use an
explicit constructor for the conversion to the argument of a copy or move
special member function, that makes the candidate worse than another.

	DR 2735
	PR c++/109247

gcc/cp/ChangeLog:

	* call.cc (sfk_copy_or_move): New.
	(joust): Add tiebreaker for explicit conv and copy ctor.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp0x/initlist-explicit3.C: New test.
This commit is contained in:
Jason Merrill 2023-05-26 12:28:15 -04:00
parent 957798e44e
commit 9872d56661
2 changed files with 46 additions and 0 deletions

View file

@ -12611,6 +12611,17 @@ cand_parms_match (z_candidate *c1, z_candidate *c2)
return compparms (parms1, parms2);
}
/* True iff FN is a copy or move constructor or assignment operator. */
static bool
sfk_copy_or_move (tree fn)
{
if (TREE_CODE (fn) != FUNCTION_DECL)
return false;
special_function_kind sfk = special_function_p (fn);
return sfk >= sfk_copy_constructor && sfk <= sfk_move_assignment;
}
/* Compare two candidates for overloading as described in
[over.match.best]. Return values:
@ -12910,6 +12921,26 @@ joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn,
return winner;
}
/* CWG2735 (PR109247): A copy/move ctor/op= for which its operand uses an
explicit conversion (due to list-initialization) is worse. */
{
z_candidate *sp = nullptr;
if (sfk_copy_or_move (cand1->fn))
sp = cand1;
if (sfk_copy_or_move (cand2->fn))
sp = sp ? nullptr : cand2;
if (sp)
{
conversion *conv = sp->convs[!DECL_CONSTRUCTOR_P (sp->fn)];
if (conv->user_conv_p)
for (; conv; conv = next_conversion (conv))
if (conv->kind == ck_user
&& DECL_P (conv->cand->fn)
&& DECL_NONCONVERTING_P (conv->cand->fn))
return (sp == cand1) ? -1 : 1;
}
}
/* or, if not that,
F1 is a non-template function and F2 is a template function
specialization. */

View file

@ -0,0 +1,15 @@
// PR c++/109247
// { dg-do compile { target c++11 } }
template <typename _Tp> struct optional {
template <typename _Up> explicit optional(_Up);
template <typename _Up = _Tp> void operator=(_Up);
};
int setPattern_pattern;
struct SourceBrush {
struct Brush {
int brush;
};
void setPattern() { m_brush = {setPattern_pattern}; }
optional<Brush> m_brush;
};