First, I define two classes, which inherits from one another.
class A {
};
class B : public A {
};
Then, I declare a function that uses an std::function<void(A*)> :
void useCallback(std::function<void(A*)> myCallback);
Finally, I receive a std::function of a different (but theoretically compatible) type from somewhere else that I would like to use in my callback function:
std::function<void(B*)> thisIsAGivenFunction;
useCallback(thisIsAGivenFunction);
My compiler (clang++) refuses this because the type of thisIsAGivenFunction doesn't match the expected type. But with B inheriting from A, it would make sense for thisIsAGivenFunction to be acceptable.
Should it be? If not, why? And if it should, then what am I doing wrong?
Let's suppose that your class hierarchy is a little bigger:
struct A { int a; };
struct B : A { int b; };
struct C : A { int c; };
and you have functions like below:
void takeA(A* ptr)
{
ptr->a = 1;
}
void takeB(B* ptr)
{
ptr->b = 2;
}
Having that, we can say that takeA is callable with any instance of class derived from A (or A itself), and that takeB is callable with any instance of class B:
takeA(new A);
takeA(new B);
takeA(new C);
takeB(new B);
// takeB(new A); // error! can't convert from A* to B*
// takeB(new C); // error! can't convert from C* to B*
Now, what std::function is, it is a wrapper for callable objects. It doesn't care much about the signature of stored function object as long as that object is callable with parameters of its std::function wrapper:
std::function<void(A*)> a; // can store anything that is callable with A*
std::function<void(B*)> b; // can store anything that is callable with B*
What you are trying to do, is to convert std::function<void(B*)> to std::function<void(A*)>. In other words, you want to store callable object taking B* within wrapper class for functions taking A*. Is there an implicit conversion of A* to B*? No, there is not.
That is, one can as well call std::function<void(A*)> with a pointer to an instance of class C:
std::function<void(A*)> a = &takeA;
a(new C); // valid! C* is forwarded to takeA, takeA is callable with C*
If std::function<void(A*)> could wrap an instance of callable object taking only B*, how would you expect it to work with C*?:
std::function<void(B*)> b = &takeB;
std::function<void(A*)> a = b;
a(new C); // ooops, takeB tries to access ptr->b field, that C class doesn't have!
Fortunately, the above code does not compile.
However, doing this the opposite way is fine:
std::function<void(A*)> a = &takeA;
std::function<void(B*)> b = a;
b(new B); // ok, interface is narrowed to B*, but takeA is still callable with B*
You can't pass &Foo(Apple) when somebody may pass you a random Fruit including a Pear.
It works but in opposite direction:
struct A {};
struct B: A {};
struct X {};
struct Y: X {};
static X useCallback(std::function<X(B)> callback) {
return callback({});
}
static Y cb(A) {
return {};
}
int main() {
useCallback(cb);
}
The signature of callback declares what will be passed to it and what is to be got back. Specific callback can take less specific types if doesn't care too much about them. Similarly it can return more specific type, extra information will be stripped. Refer to covariant vs contravariant types (input/output in simplified wording).
Related
#include <iostream>
struct A {
virtual void a() {
puts("A");
}
};
struct B {
virtual void b() {
puts("B");
}
};
struct C {
virtual void c() {
puts("C");
}
};
struct D : public A, public B, public C {
virtual void c() {
C::c();
puts("cd");
}
};
int main() {
A* obj = new D;
obj->a();
B* b = (B*)obj;
b->b();
C* c = (C*)obj;
c->c();
return 0;
}
I have this code where I have non virtual multiple inheritance. However, it seems to call the wrong virtual function when I call the functions in the main function.
Instead of outputting:
A
B
C
cd
It outputs:
A
A
A
What puzzles me is that when I change the code to doing this:
B* b = (B*)(D*)obj;
b->b();
C* c = (C*)(D*)obj;
c->c();
It outputs what I would expect (see above). Afaik doing a double pointer cast like this wouldn't effect anything and would be optimized out by the compiler. But it seems to be changing what virtual function is being called.
Can someone explain why this would change what virtual function is being called?
Notes:
I printed the pointers at each step, they are the same.
I want to avoid using dynamic_cast (although it does work) as it's too slow for what I need it to do.
Can someone explain why this would change what virtual function is being called?
Generally, a C-style cast between pointer types won't change the value of the pointer and so will have no effect. There is, however, one exception.
A cast between a class and a parent or child class can change the value of the pointer. For example:
class A
{ int a; };
class B
{ int b; };
class C : public A, public B
...
Now, a pointer to an instance of class A will probably have the same value as a pointer to its a member and a pointer to an instance of class B will probably have the same value as a pointer to its b member. A pointer to an instance of class C can't have the same value as a pointer to both its A::a and its B::b members since they're distinct objects.
A function expecting a B* can be passed a C* since a C is a B. Similarly, a function expecting an A* can be passed a C* for the same reason. But at least one of these will require a value change to the pointer.
So casts between these types will change the values, the others are all no-ops.
Of course, all of this is UB. You are casting between unrelated types and then dereferencing them.
I want to avoid using dynamic_cast (although it does work) as it's too slow for what I need it to do.
That seems very hard to believe.
I have a following abstract class
class A {
public:
virtual void foo(A* a) = 0;
}
and several classes inheriting from this class. e.g
class B : public A {
public:
void foo(A* a); // implementation in a separete file
}
However, I only want class B to accept itself as an argument in foo
void foo(B* b);
Is it possible to do this in C++?
I've considered a template but the syntax allows too much flexibility. It is possible to write class B: public A<B>, but I want a compiler error with class B: public A<C>.
-- Edit --
It seems like my use of abstract class is not justified. Let me clarify my situation.
I am utilizing a polymorphic behavior of A in a separate function. In addition to that, I want to define a function that takes in an argument of the same type such as the one above. I am trying to write a function that defines the distance between two objects of a derived class. Distance is only defined between objects from the same class (b1 and b2, or c1 and c2, but not b1 and c2). I also would like to access this distance function in a general way as possible.
-- Edit 2--
Cássio showed why it is not possible to perform compiler based checking. zar's solution adds slightly more structure to the code with runtime error checking.
I understand your question is more about the syntax. What you have is right, just pass an object of type B. The definition will still say A but it will be happy to take the derived class. You don't need any special definition for this.
class A {
public:
virtual void foo(A* a) = 0;
};
class B : public A {
public:
void foo(A* a)
{
if (dynamic_cast<B*> (a) == NULL)
std::cout << "wrong type, expecting type B\r\n";
}
};
class C : public A {
public:
void foo(A* a)
{
if (dynamic_cast<C*> (a) == NULL)
std::cout << "wrong type, expecting type C\r\n";
}
};
int main()
{
B * b1 = new B;
B * b2 = new B;
C * c1 = new C;
C * c2 = new C;
b2->foo(c1); // bad
c1->foo(b1); // bad
b2->foo(b1); // good
delete b1;
delete b2;
delete c1;
delete c2;
}
see also dynamic_cast.
That's not what virtual is for.
virtual is there to enable polymorphic behavior. Basically, to enable this:
struct A {virtual void foo()=0;};
// Two different "behaviors" for the same "A"
struct B {void foo() override{}};
struct C {void foo() override{}};
// forgive the leak, this is just to prove a point.
A* b = new B();
A* c = new C();
b->foo(); // Will call B::foo, even though this is a pointer to "A"
c->foo(); // Will call C::foo, even though this is a pointer to "A"
The way you're trying to use it, you lose this benefit, and you just get the performance hit of virtual functions for nothing. The fact that instantiating a class that doesn't implement some pure virtual function is an error is merely to prevent ill-formed programs.
If you want to make sure B implements some interface, simply use that interface somewhere. If B does not implement it, you will get the compiler error you're looking for:
class B {};
template<typename T> void call_foo(T* v1, T* v2) {
v1->foo(&v2);
}
B b1;
B b2;
b1.foo(&b2); // error
call_foo(&b1, &b2); // error
Then, to get rid of the error, you can just implement the function. No virtual needed:
class B {
void foo(B*) {/*do something*/}
};
B b1;
B b2;
b1.foo(&b2); // ok
call_foo(&b1, &b2); // ok
But, why can't I use a virtual function for this?
Imagine the following scenario:
struct A {virtual void foo(A*)=0;};
// Imagine if the language allowed this:
struct B {void foo(B*) override{}};
struct C {void foo(C*) override{}};
// (...)
// I create a vector of objects, and insert three of them in this vector.
std::vector<A*> objects;
// Note that foo is well-defined only for the first two.
objects.push_back(new B();)
objects.push_back(new B();)
objects.push_back(new C();)
// Then I shuffle the vector
std::shuffle(objects.begin(), objects.end());
// At least one of these three lines should give a compiler error.
// Which one(s)?
objects[0]->foo(objects[1]);
objects[0]->foo(objects[2]);
objects[1]->foo(objects[2]);
But I need the function to be virtual, and I need type safety!
Virtual functions are a runtime mechanism. You will have to check the type at runtime. zar's answer already covers this up nicely, so I won't get into the details. To sum it up: simply dynamic_cast into the type you want, and if the cast returns nullptr, you have the wrong type. You can then throw an exception or print some diagnostic message.
I have classes A, B, C.
B and C are derived from A. B has a function foo().
If I make an A* array and fill it with B*-s and C*-s then I can't call foo() on the B* element because the compiller will search for it in A.
Is there a way to do it, or A must contain a foo() function too?
The function foo() is only known for B objects. This means that you have a pointer to A, you can't be sure the object has such a function or not. This is why the compiler will complain with an error.
The solution to your issue is polymorphism.
Alternative 1: make the function virtual in A
With this approach, you'd have an empty foo() function that does nothing for all A and C objects, but you'd override with the correct function in B.
Example:
struct A {
virtual void foo () { cout<<"nothing!"<<endl; }
};
struct B : A {
void foo () override { cout<<"this is a B object: foo!"<<endl; }
};
struct C : A {};
int main() {
vector<A*> va;
va.push_back (new A);
va.push_back (new B);
va.push_back(new C);
for (auto x : va)
x->foo();
}
Here the online demo
For the records, I've use a vector of pointers instead of an array. But the principle is the same.
Note also that a rule of thumb is that, if you have a virtual function in a class, you should have a virtual destructor as well (I omit it here for the sake of simplicity).
Alternative 2: make the class polymorphic and use dynamic_cast
With this approach, you'd define the foo() only for B object. The trick is when you iterate through your container, you check if the object is a B (this requires the object to be polymorphic), and if yes, you invoke the function.
Example:
struct A {
virtual ~A() {}; // to make A and its descendents polymorphic, you need at least one virtual function
};
struct B : A {
void foo () { cout<<"this is a B object: foo!"<<endl; }
};
struct C : A {};
int main() {
vector<A*> va;
va.push_back (new A);
va.push_back (new B);
va.push_back(new C);
for (auto x : va) {
auto maybe = dynamic_cast<B*>(x);
if (maybe) // yes, it's a B*
maybe->foo();
else cout << "still not a B"<<endl;
}
return 0;
}
Here the online demo
The dynamic_cast is an intelligent cast: if the type of the object pointed doesn't match the target type, then dynamic_cast returns nullptr.
Alternative 3: not recommended
The last alternative can be considered if A can't be polymorphic, but if you have a mean knowing an A* to determine if the object is in reaity a B or not. This could be the case, if in A you'd have some information about the type of the object. In this case you could consider a static_cast.
This is however not recommended:
you'd have to manage yourself a way to know the type of the object (so you'd manage manually, what the compiler does automatically when the types are polymorphic).
if you'd make an error in your static_cast, i.e. you think the object is a B but in reality it is not, you'd have an undefined behavior.
I've just through a massive refactoring of a project to add a base class in place of what is now a derived class of said base class (because I want more "types" of this class).
My problem is, some of the utility functions take a reference of the original class A as a shared_ptr and so a function declaration looks as follows:
void UtilityClass::func(shared_ptr<A> &a);
But now that I have the new base class B and A is derived from B (as well as a new class C which is derived from B) I'm getting a compile-time error when I try and pass an instance of A or C to the function whose declaration is now:
void UtilityClass::func(shared_ptr<B> &b);
If I try and pass:
shared_ptr<A> a;
utilclass.func(a);
I get a compile-time error saying that (paraphrase):
Cannot convert parameter 1 from 'std::shared_ptr<_Ty>' to
'std::shared_ptr<_Ty>'
But I'm not sure how else I'd solve this problem, func() adds the A, B or C instance to a std::vector of shared_ptr values.
Thanks for your time.
EDIT: I also have another function that takes a reference so that it can assign a new instance of B to it and then return it.
The problem is that you're passing by non-const reference, which means you need the argument type to match exactly.
Change the function to take the pointer by value or const reference; then implicit conversions (such as shared_ptr<Derived> to shared_ptr<Base>) can be applied to the argument.
The following works without any problems and is a supported scenario:
#include <tchar.h>
#include <memory>
#include <iostream>
class Foo { };
class Bar : public Foo { };
int _tmain()
{
std::shared_ptr<Bar> b(new Bar());
std::cout << b.use_count() <<std::endl;
std::shared_ptr<Foo> f(b);
std::cout << b.use_count() <<std::endl;
std::cout << f.use_count() <<std::endl;
return 0;
}
If the classes are derived, no problems should occur.
Change shared_ptr<A> a; to shared_ptr<B> a;
You can still assign it a pointer of the more derived type, but achieve polymorphism via the base class.
This works:
class A {
public:
virtual ~A() {};
virtual void print() {
puts("A prints");
}
};
class B: public A {
public:
void print() {
puts("B prints");
}
};
void func(std::shared_ptr<A> a) {
a->print();
}
int main()
{
std::shared_ptr<A> b_1(new B()); // can hold B*
std::shared_ptr<B> b_2(new B());
// both next function calls print "B prints"
func(b_1);
func(b_2); // can accept std::shared_ptr<B>
}
I have a class C that can be converted to class A and a function that takes an A* as an argument. I want to call it with a C*, but I can't seem to get a conversion constructor to work. I get: error: cannot convert ‘C*’ to ‘A*’ for argument ‘1’ to ‘void doSomething(A*)’. What am I doing wrong?
class C {
};
class A {
public:
A(C* obj) {}
};
void doSomething(A* object);
int main()
{
C* object = new C();
doSomething(object);
}
Conversion constructors can only be defined for user defined types, in your case A. However, they do not apply to fundamental types as pointers like A*.
If doSomething was taking an A const& instead (or simply an A), then the conversion constructor would be invoked as you expect.
If you main requirement is to be able to call the existing doSomething function, then you can do this:
int main()
{
C* object = new C();
A a(object);
doSomething(&a);
// May need to delete object here -- depends on ownership semantics.
}
You probably mean that you want C to be a subclass of A:
class C : public A {
...
};