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.
Related
I am making a project, which requires multiple classes, some of them are inherited!!
So i am using one inherited class in a main class before it's definition!
Kindly help me to how to use prototype declaration for inherited class.thanks!!
Example:
class A
{
/*data*/
};
class B:public A
{public:
void func()
{
C obj;
}
};
class C:public B
{
};`
Only functions have prototypes. What you can do with class is to forward declare it.
class MyFutureClass;
struct MyFutureStruct;
These is an incomplete classes. No, that structs in C++ are same as classes, just they have public access by default, classes have private. To make them complete you should provide definition anywhere further in code.
Incomplete class is just one possible incomplete type. The following types are incomplete types:
type void;
class type that has been declared but not defined;
array of unknown size;
array of elements of incomplete type;
enumeration type until its underlying type is determined.
Now what you can use incomplete class for?
declare a pointer variable or class field
Declare a function or method that accepts argument or returns of incomplete type, without defining it.
what you can't do with it?
Inherit incomplete class by other class
Define objects fields of incomplete type
Declare non-static class data member of incomplete type
Use incomplete class type as parameter for template
Define functions that use argument of incomplete type or
Perform implicit or explicit conversion to incomplete class, implicit or explicit, lvalue-to-rvalue conversions, standard conversion, dynamic_cast, or static_cast to pointer or reference of incomplete type, except when converting from the null pointer constant or from a pointer to void (void* always can be converted and void never is defined)
Access incomplete class' members or methods
Use new expressions that create object of incomplete type
Use it in catch-clause
Use typeid, sizeof, or alignof operator
Use arithmetic operator on pointer to incomplete class (because sizof isn't known , of course)
What it is needed for?
To avoid cyclic dependency of headers, when one class depends on existence and functionality of other
To reduce amount of headers included in single file. You can define class that got pointers and formal parameters of declared class without defining it
let's see, this is an absolutely useless example of avoid cyclic use of headers.
[class_a.h]
class B;
class A
{
B* p;
public:
A ( B& value );
};
[class_b.h]
#include "class_a.h"
class B : public A // we need complete A to inherit it
{
int a;
public:
B ( const B& );
};
[class_a.cpp]
#include "class_b.h"
// to define this constructor we need complete A, but header with complete
// definition is already included within class_b.h header
A::A ( B& val) : p( new B(val)) // we created copy of B
{
}
So, to define class A we need class B, but class B is child of class A, thus it needs complete definition of A. Conundrum? No. Thanks to forward declaration, we can define class A without defining B.
(PS. If someone reviewed code and found something impossible there, my explicit allowance to fix it is due... I'm falling asleep)
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
Consider following example
#include <iostream>
struct PureVirtual {
virtual void Function() = 0;
};
struct FunctionImpl {
virtual void Function() {
std::cout << "FunctionImpl::Function()" << std::endl;
}
};
struct NonPureVirtual : public FunctionImpl, public PureVirtual {
using FunctionImpl::Function;
};
int main() {
NonPureVirtual c;
c.Function();
}
Compiler (GCC 4.9, Clang 3.5) exits with error
test.cpp:18:20: error: variable type 'NonPureVirtual' is an abstract class
NonPureVirtual c;
^
test.cpp:4:18: note: unimplemented pure virtual method 'Function' in 'NonPureVirtual'
virtual void Function() = 0;
^
But when I don't derive form PureVirtual everything is OK. This is weird because Standard 10.4.4 says
A class is abstract if it contains or inherits at least one pure
virtual function for which the final overrider is pure virtual.
They are not saying anything about what the final overrider is but I suppose it should be FunctionImpl::Function() especially when I made it available through using directive. So why is still NonPureVirtual abstract class and how can I fix this.
FunctionImpl::Function and PureVirtual::Function are different functions from different classes.
Their respective types are void (FunctionImpl::*)() and void (PureVirtual::*)().
Since PureVirtual and FunctionImpl are unrelated classes, these function types are unrelated.
They happen to have the same name and the same parameters and return type, but since they're different, the using FunctionImpl::Function line doesn't make that function an override of the one in PureVirtual.
And if you declared a variable of type void (PureVirtual::*)(), you wouldn't be able to assign FunctionImpl::Function to it.
In other words, the final override of PureVirtual::Function is the original one in PureVirtual, which is pure virtual.
For making what you want possible, Matthieu M.'s answer (using a forwarding call) is the way to go, it seems.
You cannot use a using directive and have to resort to a fowarding call instead:
struct NonPureVirtual : public FunctionImpl, public PureVirtual {
virtual void Function() override {
return FunctionImpl::Function();
}
};
And yes, it works as expected.
You're attributing a using declaration with something that it doesn't do. What it does is (From draft n3337, 7.3.3/1):
... using-declaration introduces a name into the declarative region in which the using-declaration appears.
Later from the same paragraph:
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 the name of some entity declared elsewhere.
In your case, FunctionImpl::Function is first declared in FunctionImpl class and it is its member. A using-declaration doesn't change that fact, as paragraph 16 says further down:
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. In particular, the implicit this parameter shall be treated as if it were a pointer to the derived class rather than to the base class. This has no effect on the type of the function, and in all other respects the function remains a member of
the base class.
Now on to the definition of overriding (10.3/2):
If a virtual member function vf is declared in a class Base and in a class Derived, derived directly or indirectly from Base, a member function vf with the same name, parameter-type-list (8.3.5), cv-qualification, and ref-qualifier (or absence of same) as Base::vf is declared, then Derived::vf is also virtual (whether or not it is so declared) and it overrides Base::vf. For convenience we say that any virtual function overrides itself.
And there's also the definition of a final overrider in the same paragraph.
A virtual member function C::vf of a class object S is a final overrider unless the most derived class (1.8) of which S is a base class subobject (if any) declares or inherits another member function that overrides vf. In a derived class, if a virtual member function of a base class subobject has more than one final overrider the program is ill-formed. [...]
I think that makes it clear that you can't use a using declaration to override a virtual function. A possible fix is in Matthieu's answer.
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.
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;
}