downcasted pointer to member as template argument - c++

I'm working in some code using the Curiously Recurring Template Pattern, where the derived class is passed up to specialize its own base classes at compile time.
With this, I run into an annoying problem which I have made a toy example for. I have annotated base::bar with comments describing the expected behavior (and why I expect it). The entire example is intended to be compiled as is, and results in 3 compiler errors (clang-3.9) as noted in the comments. For reference, $4.11/2:
A prvalue of type “pointer to member of B of type cv T”, where B is a class type, can be converted to a
prvalue of type “pointer to member of D of type cv T”, where D is a derived class (Clause 10) of B. If B is
an inaccessible (Clause 11), ambiguous (10.2), or virtual (10.1) base class of D, or a base class of a virtual
base class of D, a program that necessitates this conversion is ill-formed. The result of the conversion refers
to the same member as the pointer to member before the conversion took place, but it refers to the base
class member as if it were a member of the derived class. The result refers to the member in D’s instance of B. Since the result has type “pointer to member of D of type cv T”, it can be dereferenced with a D object.
The result is the same as if the pointer to member of B were dereferenced with the B subobject of D. The
null member pointer value is converted to the null member pointer value of the destination type.57
#include <type_traits>
template<typename impl_t>
struct super
{
typedef int impl_t::* member_dat_t;
protected:
template<member_dat_t dat>
int foo()
{
return static_cast<impl_t *>(this)->*dat;
}
};
template<typename impl_t>
struct base : super<impl_t>
{
using this_t = base<impl_t>;
int base_dat = 0;
int bar()
{
// This, of course, succeeds
this-> template foo<&impl_t::derived_dat>();
// This fails during template instantiation, because the compiler knows that the
// location of base_dat/base_func is in the base<derived> subobject of a derived
// object rather than derived itself.
this-> template foo<&impl_t::base_dat>();
// (i.e., this succeeds, despite the address being taken on a member resolved off the impl_t)
static_assert(std::is_same<int this_t::*, decltype((&impl_t::base_dat))>::value, "");
// But what if we cast, as the standard (N3242) permits in $4.11/2 [conv.mem]
// Now, these succeed
static_assert(std::is_same<int impl_t::*, decltype((static_cast<int impl_t::*>(&impl_t::base_dat)))>::value, "");
static_assert(std::is_same<typename super<impl_t>::member_dat_t, decltype((static_cast<int impl_t::*>(&impl_t::base_dat)))>::value, "");
// But these still fail
this-> template foo<static_cast<int impl_t::*>(&impl_t::base_dat)>();
this-> template foo<static_cast<typename super<impl_t>::member_dat_t>(&impl_t::base_dat)>();
return 1;
}
};
struct derived : base<derived>
{
int derived_dat;
};
void test()
{
derived d;
d.bar();
}
For the "why": the existing code in base only ever instantiates super::foo with members actually defined in derived. It would please me if I could also use members defined in base<derived>, inherited by derived. The actual code is using member functions, but member data sufficed for the example.
Edit:
I've technically come up with a solution that should work. Basically, base derives from super<base<impl_t>> instead of super<impl_t>, so now we can invoke foo with members of base<impl_t>, and if we need different behavior from the most-derived classes, we can make the methods in base virtual. But this throws away a little of the benefit of CRTP (we now have VTBL in the objects, and the cost of dynamic polymorphism, even when we know at compile time what we want).
Thanks

Related

Can you static_cast "this" to a derived class in a base class constructor then use the result later?

We ran into this scenario in our codebase at my work, and we had a big debate over whether this is valid C++ or not. Here is the simplest code example I could come up with:
template <class T>
class A {
public:
A() { subclass = static_cast<T*>(this); }
virtual void Foo() = 0;
protected:
T* subclass;
};
class C : public A<C> {
public:
C(int i) : i(i) { }
virtual void Foo() { subclass->Bar(); }
void Bar() { std::cout << "i is " << i << std::endl; }
private:
int i;
};
int main() {
C c(5);
c.Foo();
return 0;
}
This code works 100% of the time in practice (as long as the template parameter type matches the subclass type), but if we run it through a runtime analyzer, it tells us that the static_cast is invalid because we're casting this to a C* but the C constructor hasn't run yet. Sure enough, if we change the static_cast to a dynamic_cast, it returns nullptr and this program will fail and crash when accessing i in Bar().
My intuition is that it should always be possible to replace static_cast with dynamic_cast without breaking your code, suggesting that the original code in fact is depending on compiler-specific undefined behavior. However, on cppreference it says:
If the object expression refers or points to is actually a base class subobject of an object of type D, the result refers to the enclosing object of type D.
The question being, is it a base class subobject of an object of type D before the object of type D has finished being constructed? Or is this undefined behavior? My level of C++ rules lawyering is not strong enough to work this out.
In my opinion this is well-defined according to the current wording of the standard: the C object exists at the time of the static_cast, although it is under construction and its lifetime has not yet begun. This would seem to make the static_cast well-defined according to [expr.static.cast]/11, which reads in part:
... If the prvalue of type “pointer to cv1 B” points to a B that is actually a base class subobject of an object of type D, the resulting pointer points to the enclosing object of type D. Otherwise, the behavior is undefined.
It doesn't say that the D object's lifetime must have begun.
We might also want to look at the explicit rule about when it becomes legal to perform an implicit conversion from derived to base, [class.cdtor]/3:
To explicitly or implicitly convert a pointer (a glvalue) referring to an object of class X to a pointer (reference) to a direct or indirect base class B of X, the construction of X and the construction of all of its direct or indirect bases that directly or indirectly derive from B shall have started and the destruction of these classes shall not have completed, otherwise the conversion results in undefined behavior. To form a pointer to (or access the value of) a direct non-static member of an object obj, the construction of obj shall have started and its destruction shall not have completed, otherwise the computation of the pointer value (or accessing the member value) results in undefined behavior.
According to this rule, as soon as the compiler starts constructing the base class A<C>, it is well-defined to implicitly convert from C* to A<C>*. Before that point, it results in UB. The reason for this, basically, has to do with virtual base classes: if the path by which A<C> is inherited by C contains any virtual inheritance, the conversion may rely on data that are set up by one of the constructors in the chain. For a conversion from base to derived, if there is indeed any virtual inheritance on the chain, static_cast will not compile, so we don't really need to ask ourselves the question, but are those data sufficient for going the other way?
I really can't see anything in the text of the standard, nor any rationale, for the static_cast in your example not being well-defined, nor in any other case of static_casting from base to derived when the reverse implicit conversion (or static_cast) would be allowed (excepting the case of virtual inheritance, which as I said before, leads to a compile error anyway).
(Would it be well-defined to do it even earlier? In most cases this won't be possible; how could you possibly attempt to static_cast from B* to D* before the conversion from D* to B* is allowed, without having obtained the B* pointer precisely by doing the latter? If the answer is that you got from D* to B* through an intermediate base class C1 whose constructor has started, but there is another intermediate base class C2 sharing the same B base class subobject and its construction hasn't started yet, then B is a virtual base class, and again, this means the compiler will stop you from then trying to static_cast from B* back down to D*. So I think there are no issues left to resolve here.)

Is it legal to cast a pointer to a partially constructed object to a pointer to a base class?

That is, is something like this always legal?
struct Derived;
struct Base { Base(Derived*); };
struct Derived : Base { Derived() : Base(this) { } };
Base::Base(Derived *self) {
if(static_cast<Base*>(self) != this) std::terminate();
}
int main() {
Derived d; // is this well-defined to never call terminate?
}
At the point that the static_cast is evaluated, self does not yet point to a Derived object—that object is under construction. E.g. if Derived had data members, their constructors would not have been called. Is the cast still guaranteed to be defined behavior, resulting in a pointer equivalent to Base's this (which does point to a fully constructed Base base class subobject)?
A standard quote that I think gets close to answering this is [conv.ptr]/3.
...The result of the conversion is a pointer to the base class subobject of the derived class object. ...
But I think there is no derived class object yet, so what happens? If it is indeed undefined, does the answer change for self != static_cast<Derived*>(this)?
(Clang and GCC compile and run this "as expected".)
This is fine: [class.cdtor]/3 says
To explicitly or implicitly convert a pointer (a glvalue) referring to an object of class X to a pointer (reference) to a direct or indirect base class B of X, the construction of X and the construction of all of its direct or indirect bases that directly or indirectly derive from B shall have started and the destruction of these classes shall not have completed, otherwise the conversion results in undefined behavior. ...
It requires that the source type (and any other bases inheriting from the destination type) have begun its constructor and not have finished its destructor. Even the initializer for the base counts as beginning the derived constructor; the standard contains a very similar example.

Public access declaration does not affect member function pointers?

I have an issue regarding access declarations under g++ (version 5.1).
class Base
{
public:
void doStuff() {}
};
class Derived : private Base
{
public:
// Using older access declaration (without using) shoots a warning
// and results in the same compilation error
using Base::doStuff;
};
template<class C, typename Func>
void exec(C *c, Func func)
{
(c->*func)();
}
int main()
{
Derived d;
// Until here, everything compiles fine
d.doStuff();
// For some reason, I can't access the function pointer
exec(&d,&Derived::doStuff);
}
g++ fails to compile the above code with:
test.cpp: In instantiation of ‘void exec(C*, Func) [with C = Derived; Func = void (Base::*)()]’:
test.cpp:24:27: required from here
test.cpp:17:4: error: ‘Base’ is an inaccessible base of ‘Derived’
(c->*func)();
Even when the function itself can be called (d.doStuff();) the pointer can't be used even though I declared the function as accessible from the outside.
Private inheritance is also important, to some extent, because the Derived class chooses to expose only a certain set of members from base(s) which are interface implementations IRL.
NB : this is a question about the language, not class design.
The problem is that &Derived::doStuff isn't actually a pointer to a member of class Derived. From [expr.unary.op]:
The result of the unary & operator is a pointer to its operand. The operand shall be an lvalue or a qualified-id.
If the operand is a qualified-id naming a non-static or variant member m of some class C with type T,
the result has type “pointer to member of class C of type T” and is a prvalue designating C::m.
doStuff is not a member of Derived. It is a member of Base. Hence it has type pointer to member of Base, or void (Base::*)(). What the using-declaration does here is simply an aid to overload resolution, from [namespace.udecl]:
For the purpose of overload resolution, the functions which are introduced by a using-declaration into a
derived class will be treated as though they were members of the derived class.
That's why d.doStuff() works. However, through the function pointer, you're trying to call a Base member function on a Derived object. There's no overload resolution here since you're using a function pointer directly, so the base class function would be inaccessible.
You might think you could just cast &Derived::doStuff to the "correct" type:
exec(&d, static_cast<void (Derived::*)()>(&Derived::doStuff));
But you can't do that either according to [conv.mem], since again Base is an inaccessible base of Derived:
A prvalue of type “pointer to member of B of type cv T”, where B is a class type, can be converted to a
prvalue of type “pointer to member of D of type cv T”, where D is a derived class (Clause 10) of B. If B is an
inaccessible (Clause 11), ambiguous (10.2), or virtual (10.1) base class of D, or a base class of a virtual base
class of D, a program that necessitates this conversion is ill-formed.
I guess the reason is that the member function is not really part of the derived class, but rather of the base class. This can be shown somehow empirically by inspecting the type of the member function pointer and comparing it with a pointer to the base member function:
cout << typeid(&Derived::doStuff).name() << endl
<< typeid(& Base::doStuff).name() << endl;
Live here.
I'm currently searching the standard for some background on this.
Barry's answer holds the respective parts of the standard.
According to the stardard [namespace.udecl]:
A using-declaration introduces a name into the declarative region in which the using-declaration appears.
If a using-declaration names a constructor (3.4.3.1), it implicitly
declares a set of constructors in the class in which the
using-declaration appears (12.9); otherwise the name specified in a
using-declaration is a synonym for a set of declarations in another
namespace or class.
So you're just introducing Base::doStuff into the Derived region, it's still a member function of Base.
Then exec is instantiated as exec<Derived, void (Base::*)()>, but it can't cast a Derived* to Base* because of the private inheritance.
From the C++11 Standard, §7.3.3 [namespace.udecl], 18:
class A
{
private:
void f( char );
public:
void f( int );
protected:
void g();
};
class B : public A
{
using A::f; // error: A::f(char) is inaccessible
public:
using A::g;
// B::g is a public synonym for A::g
};
Note the B::g is a public synonym for A::g part. When you take the address of Derived::doStuff, GCC is creating a pointer to member function of type void(Base::*)(), and the standard says it's doing well. So, I think the compile time error is fair.

Casting a pointer to a method of a derived class to a pointer to a method of a base class

Is it legal to cast a pointer to a method of derived class to a pointer to a method of base class, even though the base class does not declare any methods, especially of the "casted" method is called through an object of type base class, as follows:
// works in VS 2008 and g++ 4.5.3
struct Base
{
};
struct Fuu : public Base
{
void bar(){ std::cout << "Fuu::bar" << std::endl; }
void bax(){ std::cout << "Fuu::bax" << std::endl; }
};
struct Foo : public Base
{
void bar(){ std::cout << "Foo::bar" << std::endl; }
void bax(){ std::cout << "Foo::bax" << std::endl; }
};
typedef void (Base::*PtrToMethod)();
int main()
{
PtrToMethod ptr1 = (PtrToMethod) &Foo::bax;
PtrToMethod ptr2 = (PtrToMethod) &Fuu::bax;
Base *f1 = new Foo;
Base *f2 = new Fuu;
(f1->*ptr1)();
(f2->*ptr2)();
}
It's worth noting that the reason the target object is contravariant is because this is effectively a parameter being passed to the function, and parameters are, in theory, contravariant (if the function can use a Base*, it can be safely plugged into any algorithm which provides only Derived* as the actual arguments).
However, for arbitrary parameters, a shim may be needed to adjust the pointer, if the base subobject is not placed at the beginning of the derived class layout. With pointer-to-members, pointer adjustment for the this pointer is built into the language. (And for this reason, pointer-to-member-of-class-with-virtual-inheritance can get quite large)
No. This is described in section 4.11 of the standard (I have n3337.pdf draft):
A prvalue of type “pointer to member of B of type cv T”, where B is a
class type, can be converted to a prvalue of type “pointer to member
of D of type cv T”, where D is a derived class (Clause 10) of B. If B
is an inaccessible (Clause 11), ambiguous (10.2), or virtual (10.1)
base class of D, or a base class of a virtual base class of D, a
program that necessitates this conversion is ill-formed. The result of
the conversion refers to the same member as the pointer to member
before the conversion took place, but it refers to the base class
member as if it were a member of the derived class. The result refers
to the member in D’s instance of B. Since the result has type “pointer
to member of D of type cv T”, it can be dereferenced with a D object.
The result is the same as if the pointer to member of B were
dereferenced with the B subobject of D. The null member pointer value
is converted to the null member pointer value of the destination
type.57
In general pointer to member conversions work in the opposite direction then pointers to derived/base classes. Pointers to (sub)objects can be converted towards the base class and pointer to methods can be converted towards more derived classes.
Please note that your program is ill formed if you attempt to call through any of the above pointers when the object involved is not of type Foo/Fuu or their derivative.
While I believe your code takes the risk it looks like similar casting (even without inheritance involved) was heavily used in OWL 2.0 library from Borland at some point.
It is legal to cast the pointer. In order to use it, you must cast it back to its original type. The underlying problem is that the pointer-to-function points to a member of the derived class; there is no guarantee that that member is a member of the base class.

C++ inheritance and member function pointers

In C++, can member function pointers be used to point to derived (or even base) class members?
EDIT:
Perhaps an example will help. Suppose we have a hierarchy of three classes X, Y, Z in order of inheritance.
Y therefore has a base class X and a derived class Z.
Now we can define a member function pointer p for class Y. This is written as:
void (Y::*p)();
(For simplicity, I'll assume we're only interested in functions with the signature void f() )
This pointer p can now be used to point to member functions of class Y.
This question (two questions, really) is then:
Can p be used to point to a function in the derived class Z?
Can p be used to point to a function in the base class X?
C++03 std, §4.11 2 Pointer to member conversions:
An rvalue of type “pointer to member of B of type cv T,” where B is a class type, can be converted to an rvalue of type “pointer to member of D of type cv T,” where D is a derived class (clause 10) of B. If B is an inaccessible (clause 11), ambiguous (10.2) or virtual (10.1) base class of D, a program that necessitates this conversion is ill-formed. The result of the conversion refers to the same member as the pointer to member before the conversion took place, but it refers to the base class member as if it were a member of the derived class. The result refers to the member in D’s instance of B. Since the result has type “pointer to member of D of type cv T,” it can be dereferenced with a D object. The result is the same as if the pointer to member of B were dereferenced with the B sub-object of D. The null member pointer value is converted to the null member pointer value of the destination type. 52)
52)The rule for conversion of pointers to members (from pointer to member of base to pointer to member of derived) appears inverted compared to the rule for pointers to objects (from pointer to derived to pointer to base) (4.10, clause 10). This inversion is necessary to ensure type safety. Note that a pointer to member is not a pointer to object or a pointer to function and the rules for conversions of such pointers do not apply to pointers to members. In particular, a pointer to member cannot be converted to a void*.
In short, you can convert a pointer to a member of an accessible, non-virtual base class to a pointer to a member of a derived class as long as the member isn't ambiguous.
class A {
public:
void foo();
};
class B : public A {};
class C {
public:
void bar();
};
class D {
public:
void baz();
};
class E : public A, public B, private C, public virtual D {
public:
typedef void (E::*member)();
};
class F:public E {
public:
void bam();
};
...
int main() {
E::member mbr;
mbr = &A::foo; // invalid: ambiguous; E's A or B's A?
mbr = &C::bar; // invalid: C is private
mbr = &D::baz; // invalid: D is virtual
mbr = &F::bam; // invalid: conversion isn't defined by the standard
...
Conversion in the other direction (via static_cast) is governed by § 5.2.9 9:
An rvalue of type "pointer to member of D of type cv1 T" can be converted to an rvalue of type "pointer to member of B of type cv2 T", where B is a base class (clause 10 class.derived) of D, if a valid standard conversion from "pointer to member of B of type T" to "pointer to member of D of type T" exists (4.11 conv.mem), and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1.11) The null member pointer value (4.11 conv.mem) is converted to the null member pointer value of the destination type. If class B contains the original member, or is a base or derived class of the class containing the original member, the resulting pointer to member points to the original member. Otherwise, the result of the cast is undefined. [Note: although class B need not contain the original member, the dynamic type of the object on which the pointer to member is dereferenced must contain the original member; see 5.5 expr.mptr.oper.]
11) Function types (including those used in pointer to member function
types) are never cv-qualified; see 8.3.5 dcl.fct.
In short, you can convert from a derived D::* to a base B::* if you can convert from a B::* to a D::*, though you can only use the B::* on objects that are of type D or are descended from D.
I'm not 100% sure what you are asking, but here is an example that works with virtual functions:
#include <iostream>
using namespace std;
class A {
public:
virtual void foo() { cout << "A::foo\n"; }
};
class B : public A {
public:
virtual void foo() { cout << "B::foo\n"; }
};
int main()
{
void (A::*bar)() = &A::foo;
(A().*bar)();
(B().*bar)();
return 0;
}
The critical issue with pointers to members is that they can be applied to any reference or pointer to a class of the correct type. This means that because Z is derived from Y a pointer (or reference) of type pointer (or reference) to Y may actually point (or refer) to the base class sub-object of Z or any other class derived from Y.
void (Y::*p)() = &Z::z_fn; // illegal
This means that anything assigned to a pointer to member of Y must actually work with any Y. If it was allowed to point to a member of Z (that wasn't a member of Y) then it would be possible to call a member function of Z on some thing that wasn't actually a Z.
On the other hand, any pointer to member of Y also points the member of Z (inheritance means that Z has all the attributes and methods of its base) is it is legal to convert a pointer to member of Y to a pointer to member of Z. This is inherently safe.
void (Y::*p)() = &Y::y_fn;
void (Z::*q)() = p; // legal and safe
You might want to check out this article Member Function Pointers and the Fastest Possible C++ Delegates The short answer seems to be yes, in some cases.
I believe so. Since the function pointer uses the signature to identify itself, the base/derived behavior would rely on whatever object you called it on.
My experimentation revealed the following: Warning - this might be undefined behaviour. It would be helpful if someone could provide a definitive reference.
This worked, but required a cast when assigning the derived member function to p.
This also worked, but required extra casts when dereferencing p.
If we're feeling really ambitious we could ask if p can be used to point to member functions of unrelated classes. I didn't try it, but the FastDelegate page linked in dagorym's answer suggests it's possible.
In conclusion, I'll try to avoid using member function pointers in this way. Passages like the following don't inspire confidence:
Casting between member function
pointers is an extremely murky area.
During the standardization of C++,
there was a lot of discussion about
whether you should be able to cast a
member function pointer from one class
to a member function pointer of a base
or derived class, and whether you
could cast between unrelated classes.
By the time the standards committee
made up their mind, different compiler
vendors had already made
implementation decisions which had
locked them into different answers to
these questions. [FastDelegate article]
Assume that we have class X, class Y : public X, and class Z : public Y
You should be able to assign methods for both X, Y to pointers of type void (Y::*p)() but not methods for Z. To see why consider the following:
void (Y::*p)() = &Z::func; // we pretend this is legal
Y * y = new Y; // clearly legal
(y->*p)(); // okay, follows the rules, but what would this mean?
By allowing that assignment we permit the invocation of a method for Z on a Y object which could lead to who knows what. You can make it all work by casting the pointers but that is not safe or guaranteed to work.
Here is an example of what works.
You can override a method in derived class, and another method of base class that uses pointer to this overridden method indeed calls the derived class's method.
#include <iostream>
#include <string>
using namespace std;
class A {
public:
virtual void traverse(string arg) {
find(&A::visit, arg);
}
protected:
virtual void find(void (A::*method)(string arg), string arg) {
(this->*method)(arg);
}
virtual void visit(string arg) {
cout << "A::visit, arg:" << arg << endl;
}
};
class B : public A {
protected:
virtual void visit(string arg) {
cout << "B::visit, arg:" << arg << endl;
}
};
int main()
{
A a;
B b;
a.traverse("one");
b.traverse("two");
return 0;
}