Does C++ final imply final in all aspects? - c++

C++11 added final.
Finally!
I understand final does two things:
Makes a class non-inheritable.
Makes (virtual) functions in a class non-overridable (in a derived class).
Both of these seem independent of each other. But take for example the following:
class Foo
{
public:
virtual void bar()
{
//do something unimportant.
}
};
class Baz final : public Foo
{
public:
void bar() /*final*/ override
{
//do something more important than Foo's bar.
}
};
From above, I believe Baz being final, I should NOT need to specify that its virtual member function bar is also final. Since Baz cannot be inherited, the question of overriding bar goes out of scope. However my compiler VC++ 2015, is very quiet about this. I have not tested this on any others at the moment.
I would be glad if someone could shed some light on this topic. A quote from the standard (if any) would be extremely appreciated. Also please state any corner cases that I am unaware of, that may cause my logical belief to fail.
So, my question is: Does a final class implicitly imply its virtual functions to be final as well? Should it? Please clarify.
The reason I am asking this is because final functions become qualified for de-virtualization, which is a great optimization. Any help is appreciated.

The reason I am asking this is because final functions become qualified for de-virtualization, which is a great optimization.
Do they? "De-virtualization" is not part of the C++ standard. Or at least, not really.
De-virtualization is merely a consequence of the "as if" rule, which states that the implementation can do whatever it likes so long as the implementation behaves "as if" it is doing what the standard says.
If the compiler can detect at compile-time that a particular call to a virtual member function, through a polymorphic type, will undeniably call a specific version of that function, then it is allowed to avoid using the virtual dispatching logic and calling the function statically. That's behaving "as if" it had used the virtual dispatching logic, since the compiler can prove that this is the function that would have been called.
As such, the standard does not define when de-virtualization is allowed/forbidden. A compiler, upon inlining a function that takes a pointer to a base class type, may find that the pointer being passed is pointing to a stack variable local declared in the function that it is being inlined within. Or that the compiler can trace down a particular inline/call graph to the point of origin for a particular polymorphic pointer/reference. In those cases, the compiler can de-virtualize calls into that type. But only if it's smart enough to do so.
Will a compiler devirtualize all virtual function calls to a final class, regardless of whether those methods are declared final themselves? It may. It may not. It may not even devirtualize any calls to methods declared final on the polymorphic type. That's a valid (if not particularly bright) implementation.
The question you're asking is implementation specific. It can vary from compiler to compiler.
However, a class being declared final, as you pointed out, ought to be sufficient information for the compiler to devirtualize all calls to pointers/references to the final class type. If a compiler doesn't do so, then that's a quality-of-implementation issue, not a standards one.

To quote the draft C++ standard from here [class.virtual/4]:
If a virtual function f in some class B is marked with the virt-specifier final and in a class D derived from B a function D::f overrides B::f, the program is ill-formed.
And here [class/3]:
If a class is marked with the class-virt-specifier final and it appears as a base-type-specifier in a base-clause (Clause [class.derived]), the program is ill-formed.
So, in answer to the question;
Does a final class implicitly imply its virtual functions to be final as well? Should it? Please clarify.
So, at least not formally. Any attempt to violate either rule will have the same result in both cases; the program is ill-formed and won't compile. A final class means the class cannot be derived from, so as a consequence of this, its virtual methods cannot be overridden.
Should it? At least formally, probably not; they are related but they are not the same thing. There is also no need formally require the one to imply the other, the effect follows naturally. Any violations have the same result, a failed compilation (hopefully with appropriate error messages to distinguish the two).
To touch on your motivation for the query and the de-virtualization of the virtual calls. This is not always immediately affected by the final of the class nor method (albeit they offer help), the normal rules of the virtual functions and class hierarchy apply.
If the compiler can determine that at runtime a particular method will always be called (e.g. with an automatic object, i.e. "on the stack"), it could apply such an optimisation anyway, irrespective of the method being marked final or not. These optimisations fall under the "as-if" rule, that allow the compiler to apply any transformation so long as the observable behaviour is as-if the original code had been executed.

Does a final class implicitly imply its virtual functions to be final as well?
[...]
I am asking this is because final functions become qualified for de-virtualization, which is a great optimization.
Yes, it does, for the purposes of de-virtualization, in all major compilers (including MSVC):
struct B { virtual void f() = 0; };
struct D1 : public B { void f(); };
struct D2 : public B { void f() final; };
struct D3 final : public B { void f(); };
void f1(D1& x) { x.f(); } // Not de-virtualized
void f2(D2& x) { x.f(); } // De-virtualized
void f3(D3& x) { x.f(); } // De-virtualized

Related

c++ statically assert that a function is never called

For some complicated reasons I want to create default constructor (alongside my normal constructors) that always throws. I want it to be there, but I also want it never to be called. It is pretty obvious that during runtime I can check for that thrown exception and for example terminate program when I catch it, but the ideal solution would be to have it checked during compilation.
So my question is: can I statically assert somehow that a function will never be called? I've looked at functions in <type_traits> but I don't see anything there that would help me. Is there some c++ dark magic that I could use to achieve my goal?
I don't have a code example, because what would even be in there?
PS. Yes. I am sure that I want to have a function and disallow everybody of calling it. As I stated previously reasons for that are complicated and irrelevant to my question.
EDIT. I can't delete this constructor or make it private. It has to be accessible for deriving classes, but they shouldn't call it. I have a case of virtual inheritance and want to "allow" calling this constructor by directly virtually derived classes (they won't call it, but c++ still requires it to be there), but no in any other classes deeper in inheritance chain.
EDIT 2. As requested I give a simplified example of my code.
#include <stdexcept>
class Base {
protected:
Base() { throw std::logic_error{"Can't be called"}; }
Base(int); // proper constructor
private:
// some members initialized by Base(int)
};
class Left: virtual public Base {
protected:
Left(int) {}
// ^ initialize members of Left, but does not call Base()!
// Though it seems that it implicitly does, Base() is never actually called.
};
class Right: virtual public Base {
protected:
Right(int) {} // The same as in Left
};
class Bottom: public Left, public Right {
public:
Bottom(int b, int l, int r): Base{b}, Left{l}, Right{r} {}
// ^ Explicitly calling constructors of Base, Left, Right.
// If I forget about calling Base(int) it silently passes
// and throws during runtime. Can I prevent this?
};
EDIT 3. Added body to Left's and Right's constructors, so that they implicitly "call" Base().
As you've stated in your comments that you never want to instatiate Base, Left or Right object, then you should make them abstract, even by some empty method:
class Base {
private:
// ...
virtual void DefineIfNonAbstract() = 0;
};
class Bottom: public Left, public Right {
void DefineIfNonAbstract() final {};
// ...
};
Trust your compiler. When it sees that DefineIfNonAbstract is private and none of its parents implemented it, it's not going to put it into a vtable.
You're Bottom class is already 16 bytes in your example for both gcc and clang (likely a pointer for each virtual inheritance). Adding the abstract method doesn't change that.
In the comments you expressed concern that this might not be safe, and sent me a link to CppCoreGuidelines:
I.25: Prefer empty abstract classes as interfaces to class hierarchies
Reason
Abstract classes that are empty (have no non-static member data) are
more likely to be stable than base classes with state.
They're referring to design choices here, not whether it causes undefined behaviour or something. In our case we're actually enforcing your design, not changing it.
The whole thing likely needs a serious rework in design. Inheritance in general is rarely a good choice - virtual inheritance even rarer.
If your link time optimization is up to the challenge, you might be able to have the problematic function call a never-defined function.
#include "Base.h"
// An anonymous namespace (or a static function) might be caught as missing
// while compiling this translation unit, so use a suggestive namespace name.
namespace DoNotDefine {
// Declare a function without a definition.
// The name is intended to make the error message easier to digest.
void DisallowedConstruction();
} // namespace DoNotDefine
Base::Base()
{
DisallowedConstruction();
}
In theory – I am not claiming that any particular linker is up to the task – the linker could eliminate unused function definitions before checking for missing definitions.
If nothing actually calls Base::Base() then such a linker would eliminate its definition before complaining that DisallowedConstruction() has no definition. After eliminating Base::Base(), there is no longer a call to DisallowedConstruction() so no problem.
If something did actually call Base::Base() then linking would fail because of there is no definition for DisallowedConstruction().
Again, I am not claiming that this will actually work with your compiler chain, only that it could work in theory.
For lesser compilers, I would suggest defining a pure virtual function in Base. Keep this pure virtual until you hit a class that uses non-default construction for Base. That ensures that no one instantiates a default Base. A bit awkward, but effective.
However, this has the drawback that the compiler cannot enforce this convention. This convention could be innocently broken. For example, someone might change a class from using non-default construction of Base to default construction and forget to remove the definition of the special function.

Is final used for optimization in C++?

class A {
public:
virtual void f() = 0;
};
class B : public A {
public:
void f() final override { };
};
int main() {
B* b = new B();
b->f();
}
In this case, is the compiler required to still do the v-table lookup for b->f();, or can it call B::f() directly because it was marked final?
Is final used for optimization in C++?
It can be, and is.
As noted, it is being used already; see here and here showing the generated code for the override with and without final.
An optimisation along these lines would relate to the "de-virtualization" of the virtual calls. This is not always immediately affected by the final of the class nor method. Albeit they offer help to determine this, the normal rules of the virtual functions and class hierarchy apply.
If the compiler can determine that at runtime a particular method will always be called (e.g. given the OP example, with an automatic object), it could apply such an optimisation anyway, irrespective of whether the method is final or not.
Optimisations fall under the as-if rule, that allow the compiler to apply any transformation so long as the observable behaviour is as-if the original code had been executed.

Performance: Inheritance and inlining? [duplicate]

I got this question when I received a code review comment saying virtual functions need not be inline.
I thought inline virtual functions could come in handy in scenarios where functions are called on objects directly. But the counter-argument came to my mind is -- why would one want to define virtual and then use objects to call methods?
Is it best not to use inline virtual functions, since they're almost never expanded anyway?
Code snippet I used for analysis:
class Temp
{
public:
virtual ~Temp()
{
}
virtual void myVirtualFunction() const
{
cout<<"Temp::myVirtualFunction"<<endl;
}
};
class TempDerived : public Temp
{
public:
void myVirtualFunction() const
{
cout<<"TempDerived::myVirtualFunction"<<endl;
}
};
int main(void)
{
TempDerived aDerivedObj;
//Compiler thinks it's safe to expand the virtual functions
aDerivedObj.myVirtualFunction();
//type of object Temp points to is always known;
//does compiler still expand virtual functions?
//I doubt compiler would be this much intelligent!
Temp* pTemp = &aDerivedObj;
pTemp->myVirtualFunction();
return 0;
}
Virtual functions can be inlined sometimes. An excerpt from the excellent C++ faq:
"The only time an inline virtual call
can be inlined is when the compiler
knows the "exact class" of the object
which is the target of the virtual
function call. This can happen only
when the compiler has an actual object
rather than a pointer or reference to
an object. I.e., either with a local
object, a global/static object, or a
fully contained object inside a
composite."
C++11 has added final. This changes the accepted answer: it's no longer necessary to know the exact class of the object, it's sufficient to know the object has at least the class type in which the function was declared final:
class A {
virtual void foo();
};
class B : public A {
inline virtual void foo() final { }
};
class C : public B
{
};
void bar(B const& b) {
A const& a = b; // Allowed, every B is an A.
a.foo(); // Call to B::foo() can be inlined, even if b is actually a class C.
}
There is one category of virtual functions where it still makes sense to have them inline. Consider the following case:
class Base {
public:
inline virtual ~Base () { }
};
class Derived1 : public Base {
inline virtual ~Derived1 () { } // Implicitly calls Base::~Base ();
};
class Derived2 : public Derived1 {
inline virtual ~Derived2 () { } // Implicitly calls Derived1::~Derived1 ();
};
void foo (Base * base) {
delete base; // Virtual call
}
The call to delete 'base', will perform a virtual call to call correct derived class destructor, this call is not inlined. However because each destructor calls it's parent destructor (which in these cases are empty), the compiler can inline those calls, since they do not call the base class functions virtually.
The same principle exists for base class constructors or for any set of functions where the derived implementation also calls the base classes implementation.
I've seen compilers that don't emit any v-table if no non-inline function at all exists (and defined in one implementation file instead of a header then). They would throw errors like missing vtable-for-class-A or something similar, and you would be confused as hell, as i was.
Indeed, that's not conformant with the Standard, but it happens so consider putting at least one virtual function not in the header (if only the virtual destructor), so that the compiler could emit a vtable for the class at that place. I know it happens with some versions of gcc.
As someone mentioned, inline virtual functions can be a benefit sometimes, but of course most often you will use it when you do not know the dynamic type of the object, because that was the whole reason for virtual in the first place.
The compiler however can't completely ignore inline. It has other semantics apart from speeding up a function-call. The implicit inline for in-class definitions is the mechanism which allows you to put the definition into the header: Only inline functions can be defined multiple times throughout the whole program without a violation any rules. In the end, it behaves as you would have defined it only once in the whole program, even though you included the header multiple times into different files linked together.
Well, actually virtual functions can always be inlined, as long they're statically linked together: suppose we have an abstract class Base with a virtual function F and derived classes Derived1 and Derived2:
class Base {
virtual void F() = 0;
};
class Derived1 : public Base {
virtual void F();
};
class Derived2 : public Base {
virtual void F();
};
An hypotetical call b->F(); (with b of type Base*) is obviously virtual. But you (or the compiler...) could rewrite it like so (suppose typeof is a typeid-like function that returns a value that can be used in a switch)
switch (typeof(b)) {
case Derived1: b->Derived1::F(); break; // static, inlineable call
case Derived2: b->Derived2::F(); break; // static, inlineable call
case Base: assert(!"pure virtual function call!");
default: b->F(); break; // virtual call (dyn-loaded code)
}
while we still need RTTI for the typeof, the call can effectively be inlined by, basically, embedding the vtable inside the instruction stream and specializing the call for all the involved classes. This could be also generalized by specializing only a few classes (say, just Derived1):
switch (typeof(b)) {
case Derived1: b->Derived1::F(); break; // hot path
default: b->F(); break; // default virtual call, cold path
}
inline really doesn't do anything - it's a hint. The compiler might ignore it or it might inline a call event without inline if it sees the implementation and likes this idea. If code clarity is at stake the inline should be removed.
Marking a virtual method inline, helps in further optimizing virtual functions in following two cases:
Curiously recurring template pattern (http://www.codeproject.com/Tips/537606/Cplusplus-Prefer-Curiously-Recurring-Template-Patt)
Replacing virtual methods with templates (http://www.di.unipi.it/~nids/docs/templates_vs_inheritance.html)
Inlined declared Virtual functions are inlined when called through objects and ignored when called via pointer or references.
With modern compilers, it won't do any harm to inlibe them. Some ancient compiler/linker combos might have created multiple vtables, but I don't believe that is an issue anymore.
A compiler can only inline a function when the call can be resolved unambiguously at compile time.
Virtual functions, however are resolved at runtime, and so the compiler cannot inline the call, since at compile type the dynamic type (and therefore the function implementation to be called) cannot be determined.
In the cases where the function call is unambiguous and the function a suitable candidate for inlining, the compiler is smart enough to inline the code anyway.
The rest of the time "inline virtual" is a nonsense, and indeed some compilers won't compile that code.
It does make sense to make virtual functions and then call them on objects rather than references or pointers. Scott Meyer recommends, in his book "effective c++", to never redefine an inherited non-virtual function. That makes sense, because when you make a class with a non-virtual function and redefine the function in a derived class, you may be sure to use it correctly yourself, but you can't be sure others will use it correctly. Also, you may at a later date use it incorrectly yoruself. So, if you make a function in a base class and you want it to be redifinable, you should make it virtual. If it makes sense to make virtual functions and call them on objects, it also makes sense to inline them.
Actually in some cases adding "inline" to a virtual final override can make your code not compile so there is sometimes a difference (at least under VS2017s compiler)!
Actually I was doing a virtual inline final override function in VS2017 adding c++17 standard to compile and link and for some reason it failed when I am using two projects.
I had a test project and an implementation DLL that I am unit testing. In the test project I am having a "linker_includes.cpp" file that #include the *.cpp files from the other project that are needed. I know... I know I can set up msbuild to use the object files from the DLL, but please bear in mind that it is a microsoft specific solution while including the cpp files is unrelated to build-system and much more easier to version a cpp file than xml files and project settings and such...
What was interesting is that I was constantly getting linker error from the test project. Even if I added the definition of the missing functions by copy paste and not through include! So weird. The other project have built and there are no connection between the two other than marking a project reference so there is a build order to ensure both is always built...
I think it is some kind of bug in the compiler. I have no idea if it exists in the compiler shipped with VS2020, because I am using an older version because some SDK only works with that properly :-(
I just wanted to add that not only marking them as inline can mean something, but might even make your code not build in some rare circumstances! This is weird, yet good to know.
PS.: The code I am working on is computer graphics related so I prefer inlining and that is why I used both final and inline. I kept the final specifier to hope the release build is smart enough to build the DLL by inlining it even without me directly hinting so...
PS (Linux).: I expect the same does not happen in gcc or clang as I routinely used to do these kind of things. I am not sure where this issue comes from... I prefer doing c++ on Linux or at least with some gcc, but sometimes project is different in needs.

Can virtual functions be inlined [duplicate]

This question already has answers here:
Are inline virtual functions really a non-sense?
(13 answers)
inline virtual function
(3 answers)
Closed 9 years ago.
If I define a class like this:
class A{
public:
A(){}
virtual ~A(){}
virtual void func(){}
};
Does it mean that that the virtual destructor and func are inlined
Whether the compiler chooses to inline a function which is defined inline is entirely up to the compiler. In general, virtual functions can only be inlined when the compiler can either prove that the static type matches the dynamic type or when the compiler can safely determine the dynamic type. For example, when you use a value of type A the compiler knows that the dynamic type cannot be different and it can inline the function. When using a pointer or a reference the compiler generally cannot prove that the static type is the same and virtual functions generally need to follow the usual virtual dispatch. However, even when a pointer is used, the compiler may have enough information from the context to know the exact dynamic type. For example, MatthieuM. gave the following exmaple:
A* a = new B;
a->func();
In this case the compiler can determine that a points to a B object and, thus, call the correct version of func() without dynamic dispatch. Without the need for the dynamic dispatch, func() could then be inlined. Of course, whether compilers do the corresponding analysis depends on its respective implementation.
As hvd correctly pointed out, the virtual dispatch can be circumvented by calling a virtual function will full qualification, e.g., a->A::func(), in which case the virtual function can also be inlined. The main reason virtual functions are generally not inlined is the need to do a virtual dispatch. With the full qualification the function to be called is, however, known.
Yes, and in multiple ways. You can see some examples of devirtualization in this email I sent to the Clang mailing list about 2 years ago.
Like all optimizations, this is pending the compiler abilities to eliminate alternatives: if it can prove that the virtual call is always resolved in Derived::func then it can call it directly.
There are various situations, let us start first with the semantic evidences:
SomeDerived& d where SomeDerived is final allows to devirtualization of all method calls
SomeDerived& d, d.foo() where foo is final also allows devirtualization of this particular call
Then, there are situations where you know the dynamic type of the object:
SomeDerived d; => the dynamic type of d is necessarily SomeDerived
SomeDerived d; Base& b; => the dynamic type of b is necessarily SomeDerived
Those 4 devirtualization situations are usually solved by the compiler front-end because they require fundamental knowledge about the language semantics. I can attest that all 4 are implemented in Clang, and I would think they are also implemented in gcc.
However, there are plenty of situations where this breaks down:
struct Base { virtual void foo() = 0; };
struct Derived: Base { virtual void foo() { std::cout << "Hello, World!\n"; };
void opaque(Base& b);
void print(Base& b) { b.foo(); }
int main() {
Derived d;
opaque(d);
print(d);
}
Even though here it is obvious that the call to foo is resolved to Derived::foo, Clang/LLVM will not optimize it. The issue is that:
Clang (front-end) does not perform inlining, thus it cannot replace print(d) by d.foo() and devirtualize the call
LLVM (back-end) does not know the semantics of the language, thus even after replacing print(d) by d.foo() it assumes that the virtual pointer of d could have been changed by opaque (whose definition is opaque, as the name implies)
I've followed efforts on the Clang and LLVM mailing list as both sets of developers reasoned about the loss of information and how to get Clang to tell LLVM: "it's okay" but unfortunately the issue is non-trivial and has not been solved yet... thus the half-assed devirtualization in the front-end to try and get all obvious cases, and some not so obvious ones (even though, by convention, the front-end is not where you implement them).
For reference, the code for the devirtualization in Clang can be found in CGExprCXX.cpp in a function called canDevirtualizeMemberFunctionCalls. It's only ~64 lines long (right now) and thoroughly commented.

name hiding and fragile base problem

I've seen it stated that C++ has name hiding for the purposes of reducing the fragile base class problem. However, I definitely don't see how this helps. If the base class introduces a function or overload that previously did not exist, it might conflict with those introduced by the derived class, or unqualified calls to global functions or member functions- but what I don't see is how this is different for overloads. Why should overloads of virtual functions be treated differently to, well, any other function?
Edit: Let me show you a little more what I'm talking about.
struct base {
virtual void foo();
virtual void foo(int);
virtual void bar();
virtual ~base();
};
struct derived : base {
virtual void foo();
};
int main() {
derived d;
d.foo(1); // Error- foo(int) is hidden
d.bar(); // Fine- calls base::bar()
}
Here, foo(int) is treated differently to bar(), because it's an overload.
I'll assume that by "fragile base class", you mean a situation where changes to the base class can break code that uses derived classes (that being the definition I found on Wikipedia). I'm not sure what virtual functions have to do with this, but I can explain how hiding helps avoid this problem. Consider the following:
struct A {};
struct B : public A
{
void f(float);
};
void do_stuff()
{
B b;
b.f(3);
}
The function call in do_stuff calls B::f(float).
Now suppose someone modifies the base class, and adds a function void f(int);. Without hiding, this would be a better match for the function argument in main; you've either changed the behaviour of do_stuff (if the new function is public), or caused a compile error (if it's private), without changing either do_stuff or any of its direct dependencies. With hiding, you haven't changed the behaviour, and such breakage is only possible if you explicitly disable hiding with a using declaration.
I don't think that overloads of virtual functions are treated any differently that overloads of regular functions. There might be one side effect though.
Suppose we have a 3 layers hierarchy:
struct Base {};
struct Derived: Base { void foo(int i); };
struct Top: Derived { void foo(int i); }; // hides Derived::foo
When I write:
void bar(Derived& d) { d.foo(3); }
the call is statically resolved to Derived::foo, whatever the true (runtime) type that d may have.
However, if I then introduce virtual void foo(int i); in Base, then everything changes. Suddenly Derived::foo and Top::foo become overrides, instead of mere overload that hid the name in their respective base class.
This means that d.foo(3); is now resolved statically not directly to a method call, but to a virtual dispatch.
Therefore Top top; bar(top) will call Top::foo (via virtual dispatch), where it previously called Derived::foo.
It might not be desirable. It could be fixed by explicitly qualifying the call d.Derived::foo(3);, but it sure is an unfortunate side effect.
Of course, it is primarily a design problem. It will only happen if the signature are compatible, else we'll have name hiding, and no override; therefore one could argue that having "potential" overrides for non-virtual functions is inviting troubles anyway (dunno if any warning exist for this, it could warrant one, to prevent being put in such a situation).
Note: if we remove Top, then it is perfectly fine to introduce the new virtual method, since all old calls were already handled by Derived::foo anyway, and thus only new code may be impacted
It is something to keep in mind though when introducing new virtual methods in a base class, especially when the impacted code is unknown (libraries delivered to clients).
Note that C++0x has the override attribute to check that a method is truly an override of a base virtual; while it does not solve the immediate problem, in the future we might imagine compilers having a warning for "accidental" overrides (ie, overrides not marked as such) in which case such an issue could be caught at compile-time after the introduction of the virtual method.
In The Design and Evolution of C++, Bjarne Stroustrup Addison-Weslay, 1994 section 3.5.3 pp 77, 78, B.S. explains that the rule by which a name in a derived class hides all definition of the same name in its base classes is old and dates back from C with Classes. When it was introduced, B.S. considered it as the obvious consequence of scoping rules (it's the same for nested blocks of code or nested namespaces — even if namespace were introduced after). The desirability of its interactions with overloading rules (the overloaded set doesn't contain the function defined in the base classes, nor in the enclosing blocks — now harmless as declaring functions in block is old fashioned —, nor in enclosing namespaces where the problem occasionally strikes as well) has been debated, to the point that G++ implemented alternative rules allowing the overloading, and B.S. argued that the current rule helps preventing errors in situations like (inspired from real live problems with g++)
class X {
int x;
public:
virtual void copy(X* p) { x = p->x; }
};
class XX: public X {
int xx;
public:
virtual void copy(XX* p) { xx = p->xx; X::copy(p); }
};
void f(X a, XX b)
{
a.copy(&b); // ok: copy X part of b
b.copy(&a); // error: copy(X*) is hidden by copy(XX*)
}
Then B.S. continues
In retrospect, I suspect that the overloading rules introduced in 2.0 might have been able to handle this case. Consider the call b.copy(&a). The variable b is an exact type match for the implicit argument of XX::copy, but requires a standard conversion to match X::copy. The variable a on the other hand, is an exact match for the explicit argument of X::copy, but requires a standard conversion to match XX:copy. Thus, had the overloading been allowed, the call would have been an error because it is ambiguous.
But I fail to see where the ambiguity is. It seems to me that B.S. overlooked the fact that &a can't be implicitly converted to a XX* and thus only X::copy has be considered.
Indeed trying with free (friends) functions
void copy(X* t, X* p) { t->x = p->x; }
void copy(XX* t, XX* p) { t-xx = p->xx; copy((X*)t, (X*)p); }
I get no ambiguity error with current compilers and I don't see how rules in the Annotated C++ Reference Manual would makes a difference here.