2

Below C++11 code won't compile (should work to my first impression) under g++ 4.9.2:

class A{};
class B : public A{};

void func(shared_ptr<B>){}
TEST(testFunc, testFunc_conv){
    std::function<void(shared_ptr<A>)> afunc;
    afunc = func;
}

The error messages indicate it doesn't accept the conversion from shared_ptr<B> to shared_ptr<A> though those B can be converted to A.

Why doesn't this work? Is it possible to work around this limitation?

EDIT I consider the implications carefully and understand the reason - A can't be converted to B indeed, so it's not allowed for the sake of type safety.

The background of this code is to implement some generic interface with variadic parameters, so other part of the world can register a callable that taking derivatives of an empty base type. The callable would be called later (kind of deferred call):

//action would be stored for deferred call
//  when action is actually called, it will use the real type
template<class ObjType>
registerInterface(function<void(Base*)> action, ObjType* obj){
    function<void()> callable = [=]{ action(obj);}
    //callable action would be stored for deferred call
}

void interfaceImpl(Derived* d){
    d->realOperation();
}

//do registration - need to do the conversion each time for each interface implementation!
Derived d;
registerInterface(interfaceImpl, &d); 

It would be annoying for each interfaceImpl to declare as taking the base type and do the downcasting brutely.

My solution is to remove the function from interface and set a implicit callable template argument for interfaceImpl to specify. Appreciate if there're better solutions.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
Fei
  • 1,450
  • 1
  • 17
  • 30

3 Answers3

4

If you want polymorphism, the declaration should include the parent class so you are able to pass its a children into it.

You are using a specific child in the definition, then trying to use the function with the base one. Obviously this would not work.

Either inherit from B or change the declaration to use the class A.


Note:

I am pretty sure you can do what you are trying to achieve in C++. However, the question you should ask yourself is: "Just because I can, should I really do it?" For example, just because you can abuse pointers to retrieve a private member of a class does not mean you should do it, and making an accessor will nearly always be the better choice.

Remember, code is being much more often read and reviewed than written. I would much rather see a straightforward code, than reading a pice of code including special constructs of the language just to make it work.

Andy
  • 1,127
  • 2
  • 12
  • 25
1

shared_ptr<B> is convertible to shared_ptr<A>. The buck stops here. Types below are not convertible:

shared_ptr<B>* to shared_ptr<A>*
void(*)(shared_ptr<B>) to void(*)(shared_ptr<A>)
function<void(shared_ptr<B>)> to function<void(shared_ptr<A>)>

This is for a good reason. What happens in the next fragment?

func (new A); // should not compile
afunc = func; // imagine this is allowed
afunc(new A); // now what?

Some of the conversions above but in the opposite direction do make sense, but C++ doesn't allow them for a number of historical and other reasons.

Fortunately you don't need any of this. You can do

template<class ObjType>

registerInterface(function action, ObjType* obj)

Or, better

registerInterface(std::function<void()> func) ... 

And then call

register_interface(std::bind(funcA,objA));  
register_interface(std::bind(funcB,objB));
n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
0

You write "those 2 types are convertible". They are not.

Since B is a subtype of A, B can be assumed to hold additional members. Therefore it is not allowed to do what you are trying. You are saying specifically that func needs a ptr to B to operate on.

Then you create a function pointer that clearly states you will send something of type A. This can not work, because as stated B being the derived class may contain more members than A, members upon func may depend (since it specifies it explicitly as the argument type).

The other way around is polymorphically ok though. You could pass type B where type A is expected, because A being the base type, this would make it certain that B has the same members, thus it is safe to pass it (since it is of type A).

Richard Tyregrim
  • 582
  • 2
  • 12