Is finalizing a virtual method possible in C++?

K

Kira Yamato

The Boost library has taught me that C++ is powerful enough (by
overloading operators and using templates) to have its syntax and
semantic modified to such an extent that seemingly impossible features
like lambda functions, variant variables, container initializers, etc.
are very possible.

So, I don't want to say too hestantly that it is impossible to simulate
the finalizing of virtual methods and classes in C++.

Hence, I rather turn to the experts and ask if anyone knows if it is
possible to simulate them in any way?

For the problem of finalizing a class, I tried the following way:

template<class A>
class finalized
{
private:
finalized() {}
friend class A; // ERROR: Cannot use template parameter in a
friend statement.
};

class CannotDerived : virtual private finalized<CannotDerived>
{
public:
CannotDerived() {}
};

But this doesn't compile for reason stated in the code. However, I can
modify the above idea into something that works:

class finalized_CannotDerived
{
private:
finalized_CannotDerived() {}

friend class CannotDerived;
};

class CannotDerived : virtual private finalized_CannotDerived
{
public:
CannotDerived() {}
};

This works but not as elegant.

Well, at least I have a way, albeit not so elegant, for finalizing a
class, but I'm still out-of-wit on how to finalize a virtual method.
Moreover, the more I think about it, the more I'm convinced it cannot
be done. I hope someone can prove me wrong here.
 
A

Alf P. Steinbach

* Kira Yamato:
Yes. Thanks. But the FAQ does not say that finalizing method is
impossible. If in fact it did, then the answer is found. Instead, it
just proposes a "solution."

So let me try asking differently, has anyone gotten further with a
better solution than merely stating a comment?

There should be no reason to finalize a member function in C++.

But if you absolutely must it's not that difficult.

class Base
{
friend class Derived;
private:
struct OverrideTag {};
public:
virtual void foo( OverrideTag = OverrideTag() ) = 0;
};

class Derived: public Base
{
public:
virtual void foo( OverrideTag = OverrideTag() ) {}
};

class FurtherDerived: public Derived
{
public:
// Nix njet.
// virtual void foo( OverrideTag = OverrideTag() ) {}
};

int main()
{
Derived o;
o.foo();
}

However, chances are that if you do something like this then you're
thinking in Java, not in C++, using ideas that don't make sense in C++.


Cheers, & hth.,

- Alf
 
K

Kira Yamato

* Kira Yamato:

There should be no reason to finalize a member function in C++.

But if you absolutely must it's not that difficult.

class Base
{
friend class Derived;
private:
struct OverrideTag {};
public:
virtual void foo( OverrideTag = OverrideTag() ) = 0;
};

class Derived: public Base
{
public:
virtual void foo( OverrideTag = OverrideTag() ) {}
};

class FurtherDerived: public Derived
{
public:
// Nix njet.
// virtual void foo( OverrideTag = OverrideTag() ) {}
};

Neat. So, it uses a similar trick as the final class method. You
should email the owner of the C++ FAQ and propose this as another
possible solution.
[...]
However, chances are that if you do something like this then you're
thinking in Java, not in C++, using ideas that don't make sense in C++.

I'm not disagreeing with you here, but I like to hear the reason why
you feel that this is not necessary (or maybe even wrong) in C++ to
finalize methods.
 
M

Marcel Müller

Alf said:
* Kira Yamato:
So let me try asking differently, has anyone gotten further with a
better solution than merely stating a comment?

There should be no reason to finalize a member function in C++. [...]
However, chances are that if you do something like this then you're
thinking in Java, not in C++, using ideas that don't make sense in C++.


I don't agree. There are performance issues.


Example:

-----
#include <stdio.h>

struct A
{ virtual void foo()
{ puts("A::foo()");
}
};

struct B : public A
{ /* final */ void foo()
{ puts("B::foo()");
}
};

int main()
{ B* x = new B;
x->foo(); // <---
delete x;
return 0;
}
-----


The line marked with "<---" could never expand the function B::foo()
inline unless the compiler knows that it is final.
On the other side a compiler may at least call any virtual member
function of a class C with a pointer of type C* as 'this' without a
vtable lookup as long as it knows that the function cannot be overwritten.

(Of course, in that particular case a global optimizer may find that x
always points to B. But that is another topic.)


Marcel
 
A

Alf P. Steinbach

* Marcel Müller:
Alf said:
* Kira Yamato:
So let me try asking differently, has anyone gotten further with a
better solution than merely stating a comment?

There should be no reason to finalize a member function in C++. [...]
However, chances are that if you do something like this then you're
thinking in Java, not in C++, using ideas that don't make sense in C++.


I don't agree. There are performance issues.


Example:

-----
#include <stdio.h>

struct A
{ virtual void foo()
{ puts("A::foo()");
}
};

struct B : public A
{ /* final */ void foo()
{ puts("B::foo()");
}
};

int main()
{ B* x = new B;
x->foo(); // <---
delete x;
return 0;
}
-----


The line marked with "<---" could never expand the function B::foo()
inline unless the compiler knows that it is final.
On the other side a compiler may at least call any virtual member
function of a class C with a pointer of type C* as 'this' without a
vtable lookup as long as it knows that the function cannot be overwritten.

(Of course, in that particular case a global optimizer may find that x
always points to B. But that is another topic.)

That's no reason.

First it's premature optimization until you've measured and found that
the virtual call is a bottleneck. Which is highly unlikely on modern
computers and with modern compilers. Check out the committee's
efficiency paper.

Second, if against all expectation that call should turn out to be a
bottleneck, one simple cure is to use a non-virtual member function:
that's what they're for, no virtual call (that's one difference between
C++ and Java ;-) ).


Cheers, & hth.,

- Alf
 
M

Marcel Müller

Alf said:
* Marcel Müller:

That's no reason.

First it's premature optimization until you've measured and found that
the virtual call is a bottleneck. Which is highly unlikely on modern
computers and with modern compilers.

Well, with arguments like this applied to all levels of software
development from the compiler to the application design, you can ensure
that we need more and more computing power even for trivial tasks. (That
is in fact what's happening. But the compilers only have a very small
part in that.)

Check out the committee's
efficiency paper.

The virtual call is no problem. But the missing inlining may be a
significant overhead in some cases. In conjunction with generic
programming trivial functions are more often used.


Second, if against all expectation that call should turn out to be a
bottleneck, one simple cure is to use a non-virtual member function:

Of course, that's a work-around without runtime overhead.

However, it's unclear to provide two interfaces. One for base class
users and another for the others.

But you are right, we should not overstate that topic.


Marcel
 
J

Joe Greer

Of course, that's a work-around without runtime overhead.

However, it's unclear to provide two interfaces. One for base class
users and another for the others.

Well if you know what class you are in, you can always invoke the
function like this:

class A
{
public:
virtual int foo() = 0;
}

class B : public A
{
public int foo() { return 5; }
};

B* p = new B;

p->B::foo();


You will find that the compiler has created a direct call to foo instead
of going through the virtual.
But you are right, we should not overstate that topic.


Marcel

Couldn't agree more. :)

joe
 
J

Jeff Schwab

Alf said:
* Kira Yamato:

There should be no reason to finalize a member function in C++.

But if you absolutely must it's not that difficult.

class Base
{
friend class Derived;
private:
struct OverrideTag {};
public:
virtual void foo( OverrideTag = OverrideTag() ) = 0;
};

class Derived: public Base
{
public:
virtual void foo( OverrideTag = OverrideTag() ) {}
};

class FurtherDerived: public Derived
{
public:
// Nix njet.
// virtual void foo( OverrideTag = OverrideTag() ) {}
};

int main()
{
Derived o;
o.foo();
}

What's to stop somebody from making OverrideTag public in the derived
class? [1]

struct Derived: Base {
using Base::OverrideTag;
virtual void foo( OverrideTag = OverrideTag() ) { }
};

[1] I was confused by something like this a couple months ago, and
posted a question about it (here, I think). I got a few explanatory
responses of the form "What do you mean you didn't think of that?
Everybody knows that, and you have no right to find it confusing." I
love the fact that people find everything obvious, just as soon as it
has been pointed out to them. :)
 
A

Alf P. Steinbach

* Jeff Schwab:
What's to stop somebody from making OverrideTag public in the derived
class?

struct Derived: Base {
using Base::OverrideTag;
virtual void foo( OverrideTag = OverrideTag() ) { }
};

Nothing. So Base just provides a service to Derived: "you can make this
function final if you want, and that's what you get by default". The
whole thing about support in Base is just because C++ doesn't have
'final', which would only appear in Derived, none of Base's business.

But I gather, if someone want's it bad enough, they can add support in
the base class...

Or introduce an artifical base class.


Cheers,

- Alf
 
B

Bo Persson

Marcel said:
Alf said:
* Kira Yamato:
So let me try asking differently, has anyone gotten further with a
better solution than merely stating a comment?

There should be no reason to finalize a member function in C++. [...]
However, chances are that if you do something like this then you're
thinking in Java, not in C++, using ideas that don't make sense in
C++.


I don't agree. There are performance issues.


Example:

-----
#include <stdio.h>

struct A
{ virtual void foo()
{ puts("A::foo()");
}
};

struct B : public A
{ /* final */ void foo()
{ puts("B::foo()");
}
};

int main()
{ B* x = new B;
x->foo(); // <---
delete x;
return 0;
}
-----


The line marked with "<---" could never expand the function B::foo()
inline unless the compiler knows that it is final.
On the other side a compiler may at least call any virtual member
function of a class C with a pointer of type C* as 'this' without a
vtable lookup as long as it knows that the function cannot be
overwritten.
(Of course, in that particular case a global optimizer may find
that x always points to B. But that is another topic.)

No, it's not really another topic!

In Java you have all functions made virtual by default. Sometimes you
might want to turn that off.

In C++, functions are only virtual if you explicitly ask for it. There
should be no need to turn if off again - just don't make them virtual
in the first place.


Bo Persson
 
J

James Kanze

Marcel Müller wrote:

[...]
In Java you have all functions made virtual by default.
Sometimes you might want to turn that off.
In C++, functions are only virtual if you explicitly ask for
it. There should be no need to turn if off again - just don't
make them virtual in the first place.

In C++, you might, in a derived class, want to declare that a
virtual function should not be overridden. Consider, for
example, a case where you use a class using the template method
pattern to implement an interface. In that class, you override
the implementations of the (pure) virtual functions in the
interface, but you do not what classes which derive from your
class to override your overrides. In such case, final would be
useful (although in practice, I've not found such accidental
overrides to be a real problem).

Conceptually, there are two orthogonal concepts: overrides, and
can be overridden. That makes four combinations:

doesn't override, cannot be overridden
doesn't override, can be overridden
overrides, cannot be overridden
overrides, can be overridden

In C++, you can't express the third; in Java, you can't express
the first two (for anything but private---for private functions,
you can't express the last three). Ideally, I'd like something
like:
/* no modifier */
virtual
overrides
final
to distinguish all four cases. But as I said, in practice, it's
not enough of a problem to warrent changing the language
(especially given the amount of code this change would break).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,013
Latest member
KatriceSwa

Latest Threads

Top