Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does overload resolution select from multiple prospective destructors?

After seeing this post: If a class has a destructor declared with a requires clause that evaluates to false, why is the class not trivially destructible? I have a separate question, and I haven't found a good answer for it yet.

The accepted answer says the class needs to declare two prospective destructors in order to compile correctly:

template<typename T>
class Optional
{
public:
  ~Optional() requires (!std::is_trivially_destructible_v<T>);
  ~Optional() = default;
};

Assuming the template is instantiated, and the requires constraint is satisfied, that means there would be 2 prospective destructors, correct? According to the C++ standard in [class.dtor]:

At the end of the definition of a class, overload resolution is performed among the prospective destructors declared in that class with an empty argument list to select the destructor for the class, also known as the selected destructor. The program is ill-formed if overload resolution fails. Destructor selection does not constitute a reference to, or odr-use ([basic.def.odr]) of, the selected destructor, and in particular, the selected destructor may be deleted ([dcl.fct.def.delete]).

So, my question is - if there are multiple prospective destructors, and they all must have an empty argument list, then HOW does overload resolution decide which prospective destructor becomes the selected destructor?

I've read:

  • What is a "prospective destructor" in C++20?
  • Prospective destructors in C++

But they don't answer my question. The accepted answer to the 1st one does state:

Which destructor declaration survives is determined later, per the part of the standard you quoted. Doing overload resolution requires doing template substitution on all of the "prospective destructors". This causes any such destructors whose requires clauses fail to disappear. If this process resolves down to a single destructor, then that's the actual destructor. And if it resolves to multiple destructors, then the code is ill-formed.

Isn't that the case in the example above - two viable destructors? Or does overload resolution deem one to be "better" than the other?

I tried reading [over.match], but don't see the answer in it. Am I missing something?

like image 789
Remy Lebeau Avatar asked Jan 01 '26 13:01

Remy Lebeau


1 Answers

Overload resolution selects the best viable function ([over.match.general]/3):

If a best viable function exists and is unique, overload resolution succeeds and produces it as the result. Otherwise overload resolution fails and the invocation is ill-formed. When overload resolution succeeds, and the best viable function is not accessible in the context in which it is used, the program is ill-formed.

Exactly how the best viable function is selected is complicated, but to prospective destructors, only [over.match.best.general]/(2.6) matters:

Given these definitions, a viable function F1 is defined to be a better function than another viable function F2 if [...], and then

  • F1 and F2 are non-template functions and F1 is more partial-ordering-constrained than F2

Since ~Optional() requires (!std::is_trivially_destructible_v<T>) has a constraint and ~Optional() does not, the former is considered more partial-ordering-constrained than the latter and thus better than the latter, and is eventually selected as the best viable function.

like image 139
cpplearner Avatar answered Jan 03 '26 03:01

cpplearner



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!