Preface:
This is a question about best practices regarding a new meaning of the delete operator introduced with C++11 when applied to a child class overriding an inherited parent's virtual method.
Background:
Per the standard, the first use case cited is to explicitly disallow calling functions for certain types where conversions would otherwise be implicit such as the example from §8.4.3 of the latest C++11 standard draft:
struct sometype {
sometype() = delete; // OK, but redundant
some_type(std::intmax_t) = delete;
some_type(double);
};
The above example is clear and purposeful. However, the following example where the new operator is overridden and prevented from being called by defining it as deleted started me thinking about other scenarios that I later identify in the question section (the example below is from §8.4.3 of the C++11 standard draft):
struct sometype {
void *operator new(std::size_t) = delete;
void *operator new[](std::size_t) = delete;
};
sometype *p = new sometype; // error, deleted class operator new
sometype *q = new sometype[3]; // error, deleted class operator new[]
Question:
By extension of this thought to inheritance, I am curious to other's thoughts regarding whether the following usage example is a clear and valid use-case or if it is an unclear abuse of the newly added feature. Please provide justification for your answer (the example that provides the most compelling reasoning will be accepted). In the example that follows, the design attempts to maintain two versions of library (the library is required to be instantiated) by having the second version of the library inherit from the first. The idea is to allow bug fixes or changes made to the first library version to automatically propagate to the second library version while allowing the second library version to focus only on its differences from the first version. To deprecate a function in the second library version, the delete operator is used to disallow a call to the overridden function:
class LibraryVersion1 {
public:
virtual void doSomething1() { /* does something */ }
// many other library methods
virtual void doSomethingN() { /* does something else */ }
};
class LibraryVersion2 : public LibraryVersion1 {
public:
// Deprecate the doSomething1 method by disallowing it from being called
virtual void doSomething1() override = delete;
// Add new method definitions
virtual void doSomethingElse() { /* does something else */ }
};
Though I can see many benefits to such an approach, I think I tend more toward the thought that it is an abuse of the feature. The primary pitfall I see in the above example is that the classic "is-a" relationship of inheritance is broken. I have read many articles that strongly recommend against any use of inheritance to express a "sort-of-is-a" relationship and to instead use composition with wrapper functions to clearly identify the relationships of the classes. While the following often-frowned-upon example requires more effort to implement and maintain (regarding the number of lines written for this piece of code, since every inherited function to be available publicly must be explicitly called out by the inheriting class), the use of delete as depicted above is very similar in many ways:
class LibraryVersion1 {
public:
virtual void doSomething1() { /* does something */ }
virtual void doSomething2() { /* does something */ }
// many other library methods
virtual void doSomethingN() { /* does something */ }
};
class LibraryVersion2 : private LibraryVersion1 {
// doSomething1 is inherited privately so other classes cannot call it
public:
// Explicitly state which functions have not been deprecated
using LibraryVersion1::doSomething2();
// ... using (many other library methods)
using LibraryVersion1::doSomethingN();
// Add new method definitions
virtual void doSomethingElse() { /* does something else */ }
};
Thank you in advance for your answers and further insight into this potential use-case of delete.
Paragraph 8.4.3/2 of the C++ Standard indirectly forbids deleting a function which overrides a virtual function:
"A program that refers to a deleted function implicitly or explicitly, other than to declare it, is ill-formed. [ Note: This includes calling the function implicitly or explicitly and forming a pointer or pointer-to-member to the function"
Invoking an overriding virtual function through a pointer to the base class is an attempt to implicitly invoke the function. Therefore, per 8.4.3/2, a design that allows this is illegal. Also notice that no C++11 conforming compiler will let you delete an overriding virtual function.
More explicitly, the same is mandated by Paragraph 10.3/16:
"A function with a deleted definition (8.4) shall not override a function that does not have a deleted definition. Likewise, a function that does not have a deleted definition shall not override a function with a deleted definition."
10.3p16:
A function with a deleted definition (8.4) shall not override a function that does not have a deleted definition. Likewise, a function that does not have a deleted definition shall not override a function with a deleted definition.
The other answers explain why pretty well, but there you have the official Thou Shalt Not.
Consider some function:
void f(LibraryVersion1* p)
{
p->doSomething1();
}
This will compile before LibraryVersion2 is even written.
So now you implement LibraryVersion2 with the deleted virtual.
f has already been compiled. It doesn't know until runtime which LibraryVersion1 subclass it has been called with.
This is why a deleted virtual isn't legal, it doesn't make any sense.
Best you can do is:
class LibraryVersion2 : public LibraryVersion1
{
public:
virtual void doSomething1() override
{
throw DeletedFunctionException();
}
}
Related
In C++ (since C++ 11 I believe), it is possible to "delete" constructors, or assignment operators, whenever the programmer does not want the compiler to automatically implement a default constructor, or assignment operator.
For example, one may,
class a
{
const a& operator=(const a& copy_me) = delete;
}
which will remove the default assignment operator. The compiler will not implement a default operator= for us. We simply will not be able to us it in our code, it will be as if it was never there.
Consider an abstract base class (or just a base class) with functions, virtual functions and pure virtuals.
class example_base
{
void function()
{
return;
}
virtual
void virtual_function() // Do we have to provide an implementation?
{
return;
}
virtual
void pure() = 0; // Are there other ways to declare a pure virtual?
// Can pure() have an implementation in this class?
}
If another class inherits from the base class, are we allowed to "delete" some of the functions provided in the base? (Q1)
If so, what are the rules and effects of this? (Q2)
What will happen if a subsequent class inherits from example_inherits and we have deleted some of the member functions? Are we allowed to use them again? (Q3)
class example_inherits : example_base
{
void function() = delete; // Allowed?
virtual
void virtual_function() = delete; // If so why ...
virtual
void pure() = delete; // ... or why not?
}
Example of Q3:
class another : example_inherits
{
// Allowed to use deleted function?
void function()
{
std::cout << "blaa" << std::endl;
}
}
You can probably think of other permutations of what I described, such as implementing a deleted method 2 orders of inheritance lower. (ie, in a class which inherits from a class which inherits from a class...)
Or different choices of virtual / pure virtual orderings... eg: one class has a virtual pure, something inherits from this class and inherits the same function as a virtual, then another class which inherits from this either deletes it or implements it as a regular function... etc...
Some insight into this would be appreciated - I wasn't able to find information this detailed online.
Well, you have quite a lot of questions here. And they all could have been answered by trying it :)
We simply will not be able to us it in our code, it will be as if it was never there
Not quite, because the deleted version participates in overload resolution. For example:
void f(long);
void f(int) = delete;
f(5);
This will fail to compile, however if you comment out the =delete version then it compiles.
Moving onto your questions.
Can pure() have an implementation in this class?
Yes, = 0 only means that derived classes must override it. But you can still provide an out-of-line definition for that function in the same class.
void function() = delete; // Allowed?
Yes, this is allowed. The derived function() hides the base function(), since it is not virtual. You could of course still call the base function through a pointer or reference to the base class.
void virtual_function() = delete;
This is not allowed; virtual functions may not be deleted.
Matt's answer covers almost everything, but I can answer the question on why you cannot delete virtual functions (Q3):
The entire point of virtual functions is to enable subtype polymorphism. A sane implementation of this implies that you can use any legally implemented subtype in any legally implemented function that is written using the base type. This is a rephrasing of the Liskov Substitution Principle.
This means if you were able to delete a virtual function in a subclass, this subclass would create undefined behavior when used in a legally implemented function. Not exactly desirable, and I can't imagine a situation where it would give any benefit either.
I have a virtual base class function which should never be used in a particular derived class. Is there a way to 'delete' it? I can of course just give it an empty definition but I would rather make its attempted use throw a compile-time error. The C++11 delete specifier seems like what I would want, but
class B
{
virtual void f();
};
class D : public B
{
virtual void f() = delete; //Error
};
won't compile; gcc, at least, explicitly won't let me delete a function that has a non-deleted base version. Is there another way to get the same functionality?
It is not allowed by the standard, however you could use one of the following two workarounds to get a similar behaviour.
The first would be to use using to change the visibility of the method to private, thus preventing others from using it. The problem with that solution is, that calling the method on a pointer of the super-class does not result in a compilation error.
class B
{
public:
virtual void f();
};
class D : public B
{
private:
using B::f;
};
The best solution I have found so far to get a compile-time error when calling Ds method is by using a static_assert with a generic struct that inherits from false_type. As long as noone ever calls the method, the struct stays undefied and the static_assert won't fail.
If the method is called however, the struct is defined and its value is false, so the static_assert fails.
If the method is not called, but you try to call it on a pointer of the super class, then Ds method is not defined and you get an undefined reference compilation error.
template <typename T>
struct fail : std::false_type
{
};
class B
{
public:
virtual void f()
{
}
};
class D : public B
{
public:
template<typename T = bool>
void
f()
{
static_assert (fail<T>::value, "Do not use!");
}
};
Another workaround would be to throw an exception when the method is used, but that would only throw up on run-time.
The standard does not allow you to delete any member of a base-class in a derived class for good reason:
Doing so breaks inheritance, specifically the "is-a" relationship.
For related reasons, it does not allow a derived class to define a function deleted in the base-class:
The hook is not any longer part of the base-class contract, and thus it stops you from relying on previous guarantees which no longer hold.
If you want to get tricky, you can force an error, but it will have to be link-time instead of compile-time:
Declare the member function but don't ever define it (This is not 100% guaranteed to work for virtual functions though).
Better also take a look at the GCC deprecated attribute for earlier warnings __attribute__ ((deprecated)).
For details and similar MS magic: C++ mark as deprecated
"I have a virtual base class function which should never be used in a particular derived class."
In some respects that is a contradiction. The whole point of virtual functions is to provide different implementations of the contract provided by the base class. What you are trying to do is break the contract. The C++ language is designed to prevent you from doing that. This is why it forces you to implement pure virtual functions when you instantiate an object. And that is why it won't let you delete part of the contract.
What is happening is a good thing. It is probably preventing you from implementing an inappropriate design choice.
However:
Sometimes it can be appropriate to have a blank implementation that does nothing:
void MyClass::my_virtual_function()
{
// nothing here
}
Or a blank implementation that returns a "failed" status:
bool MyClass::my_virtual_function()
{
return false;
}
It all depends what you are trying to do. Perhaps if you could give more information as to what you are trying to achieve someone can point you in the right direction.
EDIT
If you think about it, to avoid calling the function for a specific derived type, the caller would need to know what type it is calling. The whole point of calling a base class reference/pointer is that you don't know which derived type will receive the call.
What you can do is simply throwing an exception in the derived implementation. For example, the Java Collections framework does this quite excessively: When an update operation is performed on a collection that is immutable, the corresponding method simply throws an UnsupportedOperationException. You can do the same in C++.
Of course, this will show a malicious use of the function only at runtime; not at compile time. However, with virtual methods, you are unable to catch such errors at compile time anyway because of polymorphism. E.g.:
B* b = new D();
b.f();
Here, you store a D in a B* variable. So, even if there was a way to tell the compiler that you are not allowed to call f on a D, the compiler would be unable to report this error here, because it only sees B.
I have a virtual base class function which should never be used in a particular derived class.
C++11 provides a keyword final which prevents a virtual function being overriden from.
Look: http://en.cppreference.com/w/cpp/language/final .
class B
{
virtual void f() final;
};
class D : public B
{
// virtual void f(); // a compile-time error
// void f() override; // a compile-time error
void f(); // non-virtual function, it's ok
};
I know that calling a virtual method from a base class constructor can be dangerous since the child class might not be in a valid state. (at least in C#)
My question is what if the virtual method is the one who initializes the state of the object ? Is it good practice or should it be a two step process, first to create the object and then to load the state ?
First option: (using the constructor to initialize the state)
public class BaseObject {
public BaseObject(XElement definition) {
this.LoadState(definition);
}
protected abstract LoadState(XElement definition);
}
Second option: (using a two step process)
public class BaseObject {
public void LoadState(XElement definition) {
this.LoadStateCore(definition);
}
protected abstract LoadStateCore(XElement definition);
}
In the first method the consumer of the code can create and initialize the object with one statement:
// The base class will call the virtual method to load the state.
ChildObject o = new ChildObject(definition)
In the second method the consumer will have to create the object and then load the state:
ChildObject o = new ChildObject();
o.LoadState(definition);
(This answer applies to C# and Java. I believe C++ works differently on this matter.)
Calling a virtual method in a constructor is indeed dangerous, but sometimes it can end up with the cleanest code.
I would try to avoid it where possible, but without bending the design hugely. (For instance, the "initialize later" option prohibits immutability.) If you do use a virtual method in the constructor, document it very strongly. So long as everyone involved is aware of what it's doing, it shouldn't cause too many problems. I would try to limit the visibility though, as you've done in your first example.
EDIT: One thing which is important here is that there's a difference between C# and Java in order of initialization. If you have a class such as:
public class Child : Parent
{
private int foo = 10;
protected override void ShowFoo()
{
Console.WriteLine(foo);
}
}
where the Parent constructor calls ShowFoo, in C# it will display 10. The equivalent program in Java would display 0.
In C++, calling a virtual method in a base class constructor will simply call the method as if the derived class doesn't exist yet (because it doesn't). So that means that the call is resolved at compile time to whatever method it should call in the base class (or classes it derived from).
Tested with GCC, it allows you to call a pure virtual function from a constructor, but it gives a warning, and results in a link time error. It appears that this behavior is undefined by the standard:
"Member functions can be called from a constructor (or destructor) of an abstract class; the effect of making a virtual call (class.virtual) to a pure virtual function directly or indirectly for the object being created (or destroyed) from such a constructor (or destructor) is undefined."
With C++ the virtual methods are routed through the vtable for the class that is being constructed. So in your example it would generate a pure virtual method exception since whilst BaseObject is being constructed there simply is no LoadStateCore method to invoke.
If the function is not abstract, but simply does nothing then you will often get the programmer scratching their head for a while trying to remember why it is that the function doesn't actually get called.
For this reason you simply can't do it this way in C++ ...
For C++ the base constructor is called before the derived constructor, which means that the virtual table (which holds the addresses of the derived class's overridden virtual functions) does not yet exist. For this reason, it is considered a VERY dangerous thing to do (especially if the functions are pure virtual in the base class...this will cause a pure-virtual exception).
There are two ways around this:
Do a two-step process of construction + initialization
Move the virtual functions to an internal class that you can more closely control (can make use of the above approach, see example for details)
An example of (1) is:
class base
{
public:
base()
{
// only initialize base's members
}
virtual ~base()
{
// only release base's members
}
virtual bool initialize(/* whatever goes here */) = 0;
};
class derived : public base
{
public:
derived ()
{
// only initialize derived 's members
}
virtual ~derived ()
{
// only release derived 's members
}
virtual bool initialize(/* whatever goes here */)
{
// do your further initialization here
// return success/failure
}
};
An example of (2) is:
class accessible
{
private:
class accessible_impl
{
protected:
accessible_impl()
{
// only initialize accessible_impl's members
}
public:
static accessible_impl* create_impl(/* params for this factory func */);
virtual ~accessible_impl()
{
// only release accessible_impl's members
}
virtual bool initialize(/* whatever goes here */) = 0;
};
accessible_impl* m_impl;
public:
accessible()
{
m_impl = accessible_impl::create_impl(/* params to determine the exact type needed */);
if (m_impl)
{
m_impl->initialize(/* ... */); // add any initialization checking you need
}
}
virtual ~accessible()
{
if (m_impl)
{
delete m_impl;
}
}
/* Other functionality of accessible, which may or may not use the impl class */
};
Approach (2) uses the Factory pattern to provide the appropriate implementation for the accessible class (which will provide the same interface as your base class). One of the main benefits here is that you get initialization during construction of accessible that is able to make use of virtual members of accessible_impl safely.
For C++, section 12.7, paragraph 3 of the Standard covers this case.
To summarize, this is legal. It will resolve to the correct function to the type of the constructor being run. Therefore, adapting your example to C++ syntax, you'd be calling BaseObject::LoadState(). You can't get to ChildObject::LoadState(), and trying to do so by specifying the class as well as the function results in undefined behavior.
Constructors of abstract classes are covered in section 10.4, paragraph 6. In brief, they may call member functions, but calling a pure virtual function in the constructor is undefined behavior. Don't do that.
If you have a class as shown in your post, which takes an XElement in the constructor, then the only place that XElement could have come from is the derived class. So why not just load the state in the derived class which already has the XElement.
Either your example is missing some fundamental information which changes the situation, or there's simply no need to chain back up to the derived class with the information from the base class, because it has just told you that exact information.
i.e.
public class BaseClass
{
public BaseClass(XElement defintion)
{
// base class loads state here
}
}
public class DerivedClass : BaseClass
{
public DerivedClass (XElement defintion)
: base(definition)
{
// derived class loads state here
}
}
Then your code's really simple, and you don't have any of the virtual method call problems.
For C++, read Scott Meyer's corresponding article :
Never Call Virtual Functions during Construction or Destruction
ps: pay attention to this exception in the article:
The problem would almost certainly
become apparent before runtime,
because the logTransaction function is
pure virtual in Transaction. Unless it
had been defined (unlikely, but
possible) the program wouldn't link: the linker would be unable to find the necessary implementation of Transaction::logTransaction.
Usually you can get around these issues by having a greedier base constructor. In your example, you're passing an XElement to LoadState. If you allow the state to be directly set in your base constructor, then your child class can parse the XElement prior to calling your constructor.
public abstract class BaseObject {
public BaseObject(int state1, string state2, /* blah, blah */) {
this.State1 = state1;
this.State2 = state2;
/* blah, blah */
}
}
public class ChildObject : BaseObject {
public ChildObject(XElement definition) :
base(int.Parse(definition["state1"]), definition["state2"], /* blah, blah */) {
}
}
If the child class needs to do a good bit of work, it can offload to a static method.
In C++ it is perfectly safe to call virtual functions from within the base-class - as long as they are non-pure - with some restrictions. However, you shouldn't do it. Better initialize objects using non-virtual functions, which are explicitly marked as being such initialization functions using comments and an appropriate name (like initialize). If it is even declared as pure-virtual in the class calling it, the behavior is undefined.
The version that's called is the one of the class calling it from within the constructor, and not some overrider in some derived class. This hasn't got much to-do with virtual function tables, but more with the fact that the override of that function might belong to a class that's not yet initialized. So this is forbidden.
In C# and Java, that's not a problem, because there is no such thing as a default-initialization that's done just before entering the constructor's body. In C#, the only things that are done outside the body is calling base-class or sibling constructors i believe. In C++, however, initializations done to members of derived classes by the overrider of that function would be undone when constructing those members while processing the constructor initializer list just before entering the constructors body of the derived class.
Edit: Because of a comment, i think a bit of clarification is needed. Here's an (contrived) example, let's assume it would be allowed to call virtuals, and the call would result in an activation of the final overrider:
struct base {
base() { init(); }
virtual void init() = 0;
};
struct derived : base {
derived() {
// we would expect str to be "called it", but actually the
// default constructor of it initialized it to an empty string
}
virtual void init() {
// note, str not yet constructed, but we can't know this, because
// we could have called from derived's constructors body too
str = "called it";
}
private:
string str;
};
That problem can indeed be solved by changing the C++ Standard and allowing it - adjusting the definition of constructors, object lifetime and whatnot. Rules would have to be made to define what str = ...; means for a not-yet constructed object. And note how the effect of it then depends on who called init. The feature we get does not justify the problems we have to solve then. So C++ simply forbids dynamic dispatch while the object is being constructed.
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.
I'm working on building Cppcheck on AIX with the xlC compiler (see previous question). Checker classes all derive from a Check class, whose constructor registers each object in a global list:
check.h
class Check {
public:
Check() {
instances().push_back(this);
instances().sort();
}
static std::list<Check *> &instances();
virtual std::string name() const = 0;
private:
bool operator<(const Check *other) const {
return (name() < other->name());
}
};
checkbufferoverrun.h
class CheckBufferOverrun: public Check {
public:
// ...
std::string name() const {
return "Bounds checking";
}
};
The problem I appear to be having is with the instances().sort() call. sort() will call Check::operator<() which calls Check::name() on each pointer in the static instances() list, but the Check instance that was just added to the list has not yet had its constructor fully run (because it's still inside Check::Check()). Therefore, it should be undefined behaviour to call ->name() on such a pointer before the CheckBufferOverrun constructor has completed.
Is this really undefined behaviour, or am I missing a subtlety here?
Note that I don't think the call to sort() is strictly required, but the effect is that Cppcheck runs all its checkers in a deterministic order. This only affects the output in the order in which errors are detected, which causes causes some test cases to fail because they're expecting the output in a particular order.
Update: The question as above still (mostly) stands. However, I think the real reason why the call to sort() in the constructor wasn't causing problems (ie. crashing by calling a pure virtual function) is that the Check::operator<(const Check *) is never actually called by sort()! Rather, sort() appears to compare the pointers instead. This happens in both g++ and xlC, indicating a problem with the Cppcheck code itself.
Yes, it's undefined. The standard specifically says so in 10.4/6
Member functions can be called from a constructor (or destructor) of an abstract class; the effect of making a virtual call (10.3) to a pure virtual function directly or indirectly for the object being created (or destroyed) from such a constructor (or destructor) is undefined.
It is true that calling a pure virtual function from a constructor is always an undefined behaviour.
The virtual pointer can not be assumed to be set until the constructor has run completely (closing "}"), and hence any call to a virtual function (or pure virtual function) has to be setup at the time of compilation itself (statically bound call).
Now, if the virtual function is pure virtual function, the compiler will generally insert its own implementation for such pure virtual function, the default behavior of which is to generate a segmentation fault. The Standard does not dictate what should be the implementation of a pure virtual function, but most of C++ compilers adopt aforesaid style.
If your code is not causing any runtime mischief demeanour, then it is not getting called in the said call sequence. If you could post the implementation code for below 2 functions
instances().push_back(this);
instances().sort();
then maybe it will help to see what's going on.
As long as object construction isn't finished, a pure virtual function may not be called. However, if it's declared pure virtual in a base class A, then defined in B (derived from A), the constructor of C (derived from B) may call it, since B's construction is complete.
In your case, use a static constructor instead:
class check {
private Check () { ... }
public:
static Check* createInstance() {
Check* check = new Check();
instances().push_back(check);
instances().sort();
}
...
}
I think your real problem is that you've conflated two things: the Checker base class, and some mechanism for registering (derived) instances of Check.
Among other things, this isn't particularly robust: I may want to use your Checker classes, but I may want to register them differently.
Maybe you could do something like this: Checker get a protected ctor (it's abstract anyway, and so only derived classes ought to be calling the Checker ctor).
Derived classes also have protected ctors, and a public static method (the "named constructor pattern") to create instances. That creating method news up a Checker subclass, and them passes it (fully created at this point) to a CheckerRegister class (which is also abstract, so users can implemented their own if need be).
You use whatever singleton pattern, or dependency injection mechanism, that you prefer, to instantiate a Checkerregister and make it available to Checker subclasses.
One simple way to do this would be to have a getCheckerRegister static method on Checker.
So a Checker subclass might look like this:
class CheckBufferOverrun: public Check {
protected:
CheckBufferOverrun : Check("Bounds checking") {
// since every derived has a name, why not just pass it as an arg?
}
public:
CheckBufferOverrun makeCheckBufferOverrun() {
CheckBufferOverrun that = new CheckBufferOverrun();
// get the singleton, pass it something fully constructed
Checker.getCheckerRegister.register(that) ;
return that;
}
If it looks like this will end up being a lot of boilerplate code, write a template. If you worry that because each template instance in C++ is a real and unique class, write a non-templated base class that will register any Checker-derived.