Date: Thu, 10 Nov 2022 22:12:57 +0000
Exploring "= delete" a bit further, I see it's usable with free functions,
not just member functions. Hence this method of checking for dead code,
taking into account overload sets, is usable in more contexts than I
initially considered.
Thanks again,
Paul
On Tue, Nov 8, 2022 at 10:39 PM Paul Fee <paul.f.fee_at_[hidden]> wrote:
> On Tue, Nov 8, 2022 at 12:47 AM Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
> wrote:
>
>> On Mon, Nov 7, 2022 at 5:57 PM Paul Fee <paul.f.fee_at_[hidden]> wrote:
>>
>>> Thanks to all for your replies.
>>> My only need for this is during refactoring. You've collectively given
>>> me enough options to address my issue without additions to C++, so we
>>> needn't go further.
>>> [...]
>>> My suggestion derived from my current use of "final". I often place it
>>> on a class (despite core-guidelines C.139) to prove that there are no
>>> derived classes. I'm then free to remove redundant use of virtual, which
>>> may for example allow the virtual destructor to evaporate, allowing
>>> application of the rule-of-zero.
>>>
>>
>> Yes, absolutely. IMO, rule C.139 doesn't have much rationale behind it.
>> In *large* codebases (like, industry-size, not hobby-project-size), I
>> think it's quite useful to mark leaf classes as `final` (this helps ensure
>> your coworkers never introduce "grandchild classes" without *really*
>> thinking about it) and even to mark some non-OOP classes as `final` if
>> they're named or used in ways that make them easily confusable with OOP
>> classes (here `final` means "Don't worry, this isn't a base class").
>>
>> Another refactor is to mark copy constructors "= delete", to prove
>>> they're never used. This allows a redundant user-declared implementation
>>> to be removed without fear of impacting behaviour.
>>>
>>
>> Yep. Speaking of refactoring and overload sets, I've found `=delete`
>> quite useful for ordinary (if bad-coding-style'd) overload sets too.
>> Suppose we have
>> (simplified from
>> https://quuxplusone.github.io/blog/2020/10/11/overloading-considered-harmful/
>> )
>>
>> void subscribe(Actor*, int, bool = false); // #1
>> void subscribe(Actor*, bool = false); // #2
>>
>> We suspect that overload #1 is unused. We comment it out, and the
>> codebase still compiles, so we think it's safe to remove. But by removing
>> it we introduced a bug! Somewhere further down in the codebase, we have a
>> call-site like this:
>> subscribe(p, 42);
>> This used to call #1 as the best match; but with #1 eliminated, it will
>> happily call #2 instead.
>> The "right" way to test whether #1 is unused is not to comment it out,
>> but to mark it `=delete`. This makes all its callers ill-formed *without*
>> changing the overload resolution, so we can be sure we're seeing all the
>> call-sites (where we hope that the set of all call-sites is empty).
>>
>> HTH,
>> Arthur
>>
>
> Thanks, that's a great tip. I'll be using "= delete" even more now whilst
> refactoring.
>
not just member functions. Hence this method of checking for dead code,
taking into account overload sets, is usable in more contexts than I
initially considered.
Thanks again,
Paul
On Tue, Nov 8, 2022 at 10:39 PM Paul Fee <paul.f.fee_at_[hidden]> wrote:
> On Tue, Nov 8, 2022 at 12:47 AM Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
> wrote:
>
>> On Mon, Nov 7, 2022 at 5:57 PM Paul Fee <paul.f.fee_at_[hidden]> wrote:
>>
>>> Thanks to all for your replies.
>>> My only need for this is during refactoring. You've collectively given
>>> me enough options to address my issue without additions to C++, so we
>>> needn't go further.
>>> [...]
>>> My suggestion derived from my current use of "final". I often place it
>>> on a class (despite core-guidelines C.139) to prove that there are no
>>> derived classes. I'm then free to remove redundant use of virtual, which
>>> may for example allow the virtual destructor to evaporate, allowing
>>> application of the rule-of-zero.
>>>
>>
>> Yes, absolutely. IMO, rule C.139 doesn't have much rationale behind it.
>> In *large* codebases (like, industry-size, not hobby-project-size), I
>> think it's quite useful to mark leaf classes as `final` (this helps ensure
>> your coworkers never introduce "grandchild classes" without *really*
>> thinking about it) and even to mark some non-OOP classes as `final` if
>> they're named or used in ways that make them easily confusable with OOP
>> classes (here `final` means "Don't worry, this isn't a base class").
>>
>> Another refactor is to mark copy constructors "= delete", to prove
>>> they're never used. This allows a redundant user-declared implementation
>>> to be removed without fear of impacting behaviour.
>>>
>>
>> Yep. Speaking of refactoring and overload sets, I've found `=delete`
>> quite useful for ordinary (if bad-coding-style'd) overload sets too.
>> Suppose we have
>> (simplified from
>> https://quuxplusone.github.io/blog/2020/10/11/overloading-considered-harmful/
>> )
>>
>> void subscribe(Actor*, int, bool = false); // #1
>> void subscribe(Actor*, bool = false); // #2
>>
>> We suspect that overload #1 is unused. We comment it out, and the
>> codebase still compiles, so we think it's safe to remove. But by removing
>> it we introduced a bug! Somewhere further down in the codebase, we have a
>> call-site like this:
>> subscribe(p, 42);
>> This used to call #1 as the best match; but with #1 eliminated, it will
>> happily call #2 instead.
>> The "right" way to test whether #1 is unused is not to comment it out,
>> but to mark it `=delete`. This makes all its callers ill-formed *without*
>> changing the overload resolution, so we can be sure we're seeing all the
>> call-sites (where we hope that the set of all call-sites is empty).
>>
>> HTH,
>> Arthur
>>
>
> Thanks, that's a great tip. I'll be using "= delete" even more now whilst
> refactoring.
>
Received on 2022-11-10 22:13:09