C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Not quite a template virtual function

From: Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
Date: Tue, 1 Oct 2024 11:50:45 -0400
On Tue, Oct 1, 2024 at 6:19 AM Frederick Virchanza Gotham via Std-Proposals
<std-proposals_at_[hidden]> wrote:

>
> class Monkey {
> public:
> virtual double Do(int const arg) { return arg * 3.0; }
> virtual int Do(char, char, char) { return 6; }
> };
>
> class BlueMonkey : public Monkey {
> public:
> virtual int Act(auto&&... args)
> {
> return this->Do( static_cast<decltype(args)>(args)... );
> }
> };
> [...]
> error: implicit templates may not be 'virtual'
> | virtual int Act(auto&&... args)
>
> [...]
> Of course this 'Act' method _looks_ _like_ a template function because
> it has 'auto' in its parameter list, but really since 'args' is passed
> directly to the 'Do' method, we know that 'Do' really has only two
> possible signatures:
>
> double Do(int);
> int Do(char, char, char);
>
> which means that 'Act' can really only have two possible signatures:
>
> double Act(int);
> int Act(char, char, char);
>

Since 'Act' is virtual, we cannot tell *what* it does with its `args`.
For example, it might only care about the arity of the pack:
    class LightBlueMonkey : public BlueMonkey {
      int Act(auto&&... args) override { return sizeof...(args); }
    };
or it might care about everything:
    class DarkBlueMonkey : public BlueMonkey {
      int Act(auto&&... args) override { (std::cout << ... << args); return
0; }
    };

So your idea isn't implementable.

"But I, the human programmer, know what 'Act' will do! I don't *intend* to
override it in any child class!" — Then you should not mark it `virtual`.
Problem solved.
This is known as the "Non-Virtual Interface Idiom" and/or the "Template
Method Pattern," and it's a very good idea. I use it myself.
The idea is to permit overriding of the little pieces of work encased in
'Do' methods, but *not* permit overriding of the overall "pattern" of how
those pieces are put together in the 'Act' method.

    class Monkey {
    private:
        virtual double Do(int const arg) = 0;
        virtual int Do(char, char, char) = 0;
    public:
        int Act(auto&&... args) {
            return this->Do( static_cast<decltype(args)>(args)... );
        }
    };



> I came up with this idea yesterday [...]
> virtual bool SetGrade(unsigned const arg) override {
> // Do logging
> return this->pPM->SetGrade(arg);
> }
> virtual bool SetLevel(unsigned const arg) override {
> // Do logging
> return this->pPM->SetLevel(arg);
> }
> [...]
> Look at how I had to write out each individual method, SetGrade and
> SetLevel (there was more like a dozen of them to write out). It would
> have been handy, if [...] I had been able to write:
>
> virtual decltype(auto) SetLevel(auto &&args) override
> {
> // Do logging
> return this->pPM->SetLevel(
> static_cast<decltype(args)>(args)... );
> }
>

That wouldn't help you; you need to also change the *name* of the function
(and the name of the function it calls) from SetLevel to SetGrade.
This is the sort of thing where people say "Reflection and code generation
will solve that!"

HTH,
Arthur

Received on 2024-10-01 15:50:59