Date: Mon, 7 Nov 2022 19:47:22 -0500
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 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
>
Received on 2022-11-08 00:47:34