C++ Standard 12.7/4 says:
When a virtual function is called directly or indirectly from a constructor or from a destructor, including during the construction or destruction of the class's non-static data members, and the object to which the call applies is the object (call it x) under construction or destruction, the function called is the final overrider in the constructor's or destructor's class and not one overriding it in a more-derived class. If the virtual function call uses an explicit class member access (5.2.5) and the object expression refers to the complete object of x or one of that object's base class subobjects but not x or one of its base class subobjects, the behavior is undefined.
This text is the same in all versions I checked (though in C++03 it was paragraph 12.7/3).
My question is about the phrase "uses an explicit class member access". Possibly the point of that phrase is to point out that in the constructor/destructor body, virtual calls that use the implicit this-> are safe since the object expression does refer to the object x:
struct A;
A* p;
struct A {
A() { p = this; }
virtual ~A() { if (p == this) p = nullptr; }
virtual void f() {}
};
struct B {
B();
virtual ~B();
virtual void g() {}
};
struct C : public A, public B {
virtual void f() {}
virtual void g() {}
};
B::B() {
if (p) p->f(); // UB if `p` and `this` point at same complete object
g(); // Definitely safe, calls B::g().
}
B::~B() {
if (p) p->f(); // UB if `p` and `this` point at same complete object
g(); // Definitely safe, calls B::g().
}
int main() {
C c; // UB in B::B() and B::~B()!
}
But what if the virtual function call is not syntactically in the definition of the constructor or destructor, but is called indirectly? What is the behavior of this program?
#include <iostream>
struct A {
virtual void f() { std::cout << "A::f()\n"; }
void h() { f(); }
};
struct B {
explicit B(A& a) { a.h(); }
};
struct C : public A, public B {
C() : A(), B(static_cast<A&>(*this)) {}
virtual void f() { std::cout << "C::f()\n"; }
};
int main() {
C c;
}
I would expect that in B::B(A&), calling a.h() is just as undefined as calling a.f(). But we can't say the last sentence in 12.7/4 applies, since the virtual function call does not use an explicit class member access. Have I missed something? Are a.f() and a.h() really supposed to act differently in this context? Is there a Defect Report related to this? Should there be?
9.3.1/3 (in N3485) says
When an id-expression (5.1) that is not part of a class member access syntax (5.2.5) and not used to form
a pointer to member (5.3.1) is used in a member of class X in a context where this can be used (5.1.1),
if name lookup (3.4) resolves the name in the id-expression to a non-static non-type member of some class
C, and if either the id-expression is potentially evaluated or C is X or a base class of X, the id-expression is
transformed into a class member access expression (5.2.5) using (*this) (9.3.2) as the postfix-expression to
the left of the . operator.
In your second example, this means the body of A::h() gets transformed into (*this).f(), making the call an explicit class member access. Thus the last line of 12.7/4 applies; the behavior is undefined.
It should not make any difference.
The standard says:
When a virtual function is called directly or indirectly
However, your compiler may well have a bug - perhaps because it optimises the code in h, thinking that it understands what is going on (and not actually doing the right thing). You haven't mentioned WHICH compiler you are using, so it's not possible to say if there is a defect report...
Edit: Both g++ 4.8.2 and clang++ 3.5 prior to release from a few weeks back (with -std=c++11, in case that makes a difference) calls C::f() in the destructor, and A::f() in the constructor for your first testcase. In the second test-case, g++ calls A::f(), where clang++ calls C::f(). So clearly, the compiler seems to do "whatever it feels like" here. [Note that since it's "undefined", it can do all sorts of different things, including "what you expect"].
(In the first test-case, I modified p to be a to make it compile, and added printouts in f and g functions)
Related
According to the C++ standard: 5.3.5/4:
If the static type of the operand [of the delete operator] is
different from its dynamic type, the static type shall be a base class
of the operand's dynamic type and the static type shall have a virtual
destructor or the behaviour is undefined.
Does this apply even for something such as:
class B
{
public:
virtual void f() const = 0;
virtual void g() = 0;
};
class D : public B
{
public:
explicit D(int x) : x_ {x} {}
void f() const override;
void g() override;
private:
int x_;
};
In this case neither D nor B has anything to deallocate, so shouldn't it no longer be required to provide virtual destructors? Or is this still undefined behavior?
That statement from the standard you quote means that a sample like (using your class B and D)
int main()
{
B *object = new D;
delete object;
}
has undefined behaviour if B does not have a virtual destructor.
No exceptions to that rule.
It doesn't matter what the classes (or their member functions) do or don't do. It is the non-virtual destructor in B that causes the delete expression (delete object) to have undefined behaviour. No exceptions.
The standard doesn't make any exceptions predicated on the contents of the classes.
I would say, "Yes, you need virtual destructors."
Is it legal C++ to create a worker-object on the stack in the destructor of some master-object and pass the this pointer of the master-object to the helper-object? The helper-object would then also call member functions of the master-object or access member-variables.
In other words, is the following legal C++?
struct MasterClass
{
MasterClass (int data);
~MasterClass ();
int data;
};
struct WorkerClass
{
WorkerClass (MasterClass *m) : m (m) { }
void do_some_work () { m->data = 42; }
MasterClass *m;
};
MasterClass::MasterClass (int data)
: data (data)
{ }
MasterClass::~MasterClass ()
{
WorkerClass w (this);
w.do_some_work ();
}
int main ()
{
MasterClass m (7);
}
I understand that the lifetime of the master-object ends once the destructor begins to execute. But I believe it is legal to call non-virtual member functions in the destructor of any object, which make use of the implicit this argument/parameter.
Yes and no.
Yes, because its legal in this very short example you've shown.
No, because it might result in UB, there are some caveats surrounding usage of an object during destruction
TLDR It's always fine if you don't have any inheritance.
Now, for the cases where it is not fine to use an object during destruction.
The following cases will assume the following is already written
struct V;
struct A;
struct B;
struct D;
void foo(A* a = nullptr);
struct V {
virtual void f();
virtual void g();
};
struct A : virtual V {
virtual void f();
};
struct B : virtual V {
virtual void g();
~B() {
foo();
}
};
struct D : A, B {
virtual void f();
virtual void g();
~D() {
foo(this);
}
};
int main() {
D d;
}
Calling virtual functions
Upon the destruction of x (aka as soon as its destructor is called)
If the virtual function call uses an explicit class member access and the object expression refers to the complete object of x or one of that object's base class subobjects but not x or one of its base class subobjects, the behavior is undefined.
Which means, if you use a explicit class member access to call a virtual function with a pointer pointing to the entirety of x, but somehow the pointer isn't the type of x nor its bases, the behaviour is undefined.
void foo(A* a) {
static auto ptr = a;
ptr->g(); // UB when called from ~B
// ptr refers to B, but is neither B nor its base
}
Using typeid
If the operand of typeid refers to the object under construction or destruction and the static type of the operand is neither the constructor or destructor's class nor one of its bases, the behavior is undefined.
Likewise, if the operand refers to the object being destructed, yet somehow isn't the object and its bases, the behaviour is undefined.
void foo(A* a) {
static auto ptr = a;
typeid(*ptr); // UB when called from ~B()
// ptr refers to B, but is neither B nor its base
}
Using dynamic_cast
If the operand of the dynamic_cast refers to the object under construction or destruction and the static type of the operand is not a pointer to or object of the constructor or destructor's own class or one of its bases, the dynamic_cast results in undefined behavior.
Same deal.
void foo(A* a) {
static auto ptr = a;
dynamic_cast<B*>(ptr); // UB when called from ~B()
// ptr refers to B, but is neither B nor its base
}
Conclusion
Now, if you think this is a fiasco and didn't understand what is going on, just don't pass this anywhere in a destructor.
All quotes from http://eel.is/c++draft/class.cdtor
Yes, this is legal, since the master object will not be destroyed before the termination of execution of the destructor.
However, this is not a good practice in general.
I want to bind() to my base class's version of a function from the derived class. The function is marked protected in the base. When I do so, the code compiles happily in Clang (Apple LLVM Compiler 4.1) but gives an error in both g++ 4.7.2 and in Visual Studio 2010. The error is along the lines of: "'Base::foo' : cannot access protected member."
The implication is that the context for the reference is actually within bind(), where of course the function is seen as protected. But shouldn't bind() inherit the context of the calling function--in this case, Derived::foo()--and therefore see the base method as accessible?
The following program illustrates the issue.
struct Base
{
protected: virtual void foo() {}
};
struct Derived : public Base
{
protected:
virtual void foo() override
{
Base::foo(); // Legal
auto fn = std::bind( &Derived::foo,
std::placeholders::_1 ); // Legal but unwanted.
fn( this );
auto fn2 = std::bind( &Base::foo,
std::placeholders::_1 ); // ILLEGAL in G++ 4.7.2 and VS2010.
fn2( this );
}
};
Why the discrepancy in behavior? Which is correct? What workaround is available for the error-giving compilers?
Answer: see boost::bind with protected members & context which quotes this part of the Standard
An additional access check beyond those described earlier in clause 11
is applied when a non-static data member or nonstatic member function
is a protected member of its naming class (11.2)105) As described
earlier, access to a protected member is granted because the reference
occurs in a friend or member of some class C. If the access is to form
a pointer to member (5.3.1), the nested-name-specifier shall name C or
a class derived from C. All other accesses involve a (possibly
implicit) object expression (5.2.5). In this case, the class of the
object expression shall be C or a class derived from C.
Workaround: make foo a public member function
#include <functional>
struct Base
{
public: virtual void foo() {}
};
This has nothing to do with bind. Because of the piece of the Standard #rhalbersma already quoted, the expression &Base::foo is illegal in a non-friended member of Derived, in every context.
But if your intent was to do something equivalent to calling Base::foo();, you have a bigger issue: pointers to member functions always invoke a virtual override.
#include <iostream>
class B {
public:
virtual void f() { std::cout << "B::f" << std::endl; }
};
class D : public B {
public:
virtual void f() { std::cout << "D::f" << std::endl; }
};
int main() {
D d;
d.B::f(); // Prints B::f
void (B::*ptr)() = &B::f;
(d.*ptr)(); // Prints D::f!
}
This question already has answers here:
C++ virtual function from constructor [duplicate]
(7 answers)
Closed 8 years ago.
I was expecting derived method get() will be called from constructor of A. Wondering why is this not happening?
Method get() is virtual in base class so derive class B will override this method.
#include <iostream>
using namespace std;
class A {
protected:
virtual int get() {
cout<<" in A::get "<<endl;
return 0;
}
public:
A() {
get();
}
};
class B : public A {
public:
B():A() {}
protected:
int get() override{
cout<<"in B:get() "<<endl;
return 0;
}
};
int main() {
A *a; a = new B();
return 0;
}
While it's possible and sometimes a good choice, it's most of the time a bad idea to call virtual functions in a constructor. Only do this if you really understand what that means. The same applies for calling virtual functions in destructors.
When the instance of class B is constructed, it first constructs an A. During this phase, the object is only identified as an instance of A rather than B. So at that time, the object has a virtual function table of class A. This means that every virtual function call will resolve to functions of class A (or super classes); the same rules apply as if you would only construct an instance of A.
For a full explanation, please read When my base class's constructor calls a virtual function on its this object, why doesn't my derived class's override of that virtual function get invoked?
Even if it was possible to call the function B::get() within the constructor of A (example: http://ideone.com/sF3411), that would be undefined behavior, since when the constructor of A is executed, the this pointer is not yet pointing to an instance of B, it's only "in preparation" if you will. The linked code might work, but it's a hack and exposes undefined behavior.
Apart from the C++ FAQ link from leemes, C++ standard prevent it to be called, I will quote the standard.
The following says that, virtual member functions can be called within constructor/destructor, but they act as "not virtual", which you already know in the C++ FAQ.
Member functions, including virtual functions (10.3), can be called
during construction or destruction (12.6.2). When a virtual function
is called directly or indirectly from a constructor or from a
destructor, including during the construction or destruction of the
class’s non-static data members, and the object to which the call
applies is the object (call it x) under construction or destruction,
the function called is the final overrider in the constructor’s or
destructor’s class and not one overriding it in a more-derived class.
If the virtual function call uses an explicit class member access
(5.2.5) and the object expression refers to the complete object of x
or one of that object’s base class subobjects but not x or one of its
base class subobjects, the behavior is undefined.
It actually also says that, when calling virtual function from a pointer (to itself) with a type that is not direct base class of itself (in multiple inheritance), the behaviour is undefined.
Example (from standard)
struct V {
virtual void f();
virtual void g();
};
struct A : virtual V {
virtual void f();
};
struct B : virtual V {
virtual void g();
B(V*, A*);
};
struct D : A, B {
virtual void f();
virtual void g();
D() : B((A*)this, this) { }
};
B::B(V* v, A* a) {
f(); // calls V::f, not A::f
g(); // calls B::g, not D::g
v->g(); // v is base of B, the call is well-defined, calls B::g
a->f(); // undefined behavior, a’s type not a base of B
}
The above rules apply to other dynamic binding stuff, including typeid, meaning that you can't use typeid to differentiate derived class type in base constructor.
The typeid operator (5.2.8) can be used during construction or
destruction (12.6.2). When typeid is used in a constructor (including
the mem-initializer or brace-or-equal-initializer for a non-static
data member) or in a destructor, or used in a function called
(directly or indirectly) from a constructor or destructor, if the
operand of typeid refers to the object under construction or
destruction, typeid yields the std::type_info object representing the
constructor or destructor’s class. If the operand of typeid refers to
the object under construction or destruction and the static type of
the operand is neither the constructor or destructor’s class nor one
of its bases, the result of typeid is undefined.
I want to bind() to my base class's version of a function from the derived class. The function is marked protected in the base. When I do so, the code compiles happily in Clang (Apple LLVM Compiler 4.1) but gives an error in both g++ 4.7.2 and in Visual Studio 2010. The error is along the lines of: "'Base::foo' : cannot access protected member."
The implication is that the context for the reference is actually within bind(), where of course the function is seen as protected. But shouldn't bind() inherit the context of the calling function--in this case, Derived::foo()--and therefore see the base method as accessible?
The following program illustrates the issue.
struct Base
{
protected: virtual void foo() {}
};
struct Derived : public Base
{
protected:
virtual void foo() override
{
Base::foo(); // Legal
auto fn = std::bind( &Derived::foo,
std::placeholders::_1 ); // Legal but unwanted.
fn( this );
auto fn2 = std::bind( &Base::foo,
std::placeholders::_1 ); // ILLEGAL in G++ 4.7.2 and VS2010.
fn2( this );
}
};
Why the discrepancy in behavior? Which is correct? What workaround is available for the error-giving compilers?
Answer: see boost::bind with protected members & context which quotes this part of the Standard
An additional access check beyond those described earlier in clause 11
is applied when a non-static data member or nonstatic member function
is a protected member of its naming class (11.2)105) As described
earlier, access to a protected member is granted because the reference
occurs in a friend or member of some class C. If the access is to form
a pointer to member (5.3.1), the nested-name-specifier shall name C or
a class derived from C. All other accesses involve a (possibly
implicit) object expression (5.2.5). In this case, the class of the
object expression shall be C or a class derived from C.
Workaround: make foo a public member function
#include <functional>
struct Base
{
public: virtual void foo() {}
};
This has nothing to do with bind. Because of the piece of the Standard #rhalbersma already quoted, the expression &Base::foo is illegal in a non-friended member of Derived, in every context.
But if your intent was to do something equivalent to calling Base::foo();, you have a bigger issue: pointers to member functions always invoke a virtual override.
#include <iostream>
class B {
public:
virtual void f() { std::cout << "B::f" << std::endl; }
};
class D : public B {
public:
virtual void f() { std::cout << "D::f" << std::endl; }
};
int main() {
D d;
d.B::f(); // Prints B::f
void (B::*ptr)() = &B::f;
(d.*ptr)(); // Prints D::f!
}