3

Why does the following code not allow foo(ptr) to be called ?

#include <boost/scoped_ptr.hpp>
struct A {
    virtual ~A() {}
};

struct B: public A {};

void foo(boost::scoped_ptr<A>& a) {}

void goo(A& a) {}
int main() {
    boost::scoped_ptr<B> ptr(new B);
    foo(ptr);
    B b;
    goo(b);
}

The corresponding form where we pass references works as expected. Are we supposed not to do polymorphism with boost scoped_ptr ?

g++ with boost 1.49 gives me:

error: invalid initialization of reference of type ‘boost::scoped_ptr<A>&’ from expression of type ‘boost::scoped_ptr<B>’
ATemp
  • 319
  • 3
  • 10

1 Answers1

5

That's because foo, for some reason, takes a scoped pointer by reference. That is completely unnecessary and is the reason why the call fails. There is a conversion from scoped_ptr<B> to scoped_ptr<A> but not from scoped_ptr<B>& to scoped_ptr<A>&.

You should pass it as reference to const.

void foo(boost::scoped_ptr<A> const & a) {}

Incidentally, this isn't a "problem" of smart pointers per se. The following code fails for the same reasons as yours.

void foo(A*& p) {}
int main()
{
    B* p = new B;
    foo(p); //FAIL
}

In order to fix this you have to pass the pointer either by value, or, if you're sufficiently perverted, by reference to const

 void foo (A * const & p); // <-- a perv wrote this
Armen Tsirunyan
  • 130,161
  • 59
  • 324
  • 434
  • wouldn't that need a copy of scoped_ptrs, which has a private copy constructor ? In any case, I want the scoped_ptr to persist in the callee. – ATemp Mar 23 '12 at 19:49
  • changing foo() to make scoped_ptr instead of reference gives me: error: conversion from ‘boost::scoped_ptr’ to non-scalar type ‘boost::scoped_ptr’ requested – ATemp Mar 23 '12 at 19:51
  • If you want it to persist in the callee then I'm pretty sure you don't want a `scoped_ptr`. You want `unique_ptr` if you're transferring ownership or `shared_ptr` if both places retain pointership to it. – Mark B Mar 23 '12 at 19:52
  • @Armen: I am puzzled - why does the const reference work and const does not ? – ATemp Mar 23 '12 at 19:53
  • @MMarkb: Perhaps ptr is a member variable in a class. We can pass the member variable in a method by reference but want the variable to be cleaned up when the object is destroyed not when the called function exists. Am I thinking incorrectly here? – ATemp Mar 23 '12 at 19:54
  • 1
    @ATemp: Because in order to pass something by reference, the types must match exactly. But if it's reference to const, then conversions can take place. C++ rules – Armen Tsirunyan Mar 23 '12 at 19:55
  • @ATemp, think of it this way; what happens if you assign an `A *` to something which appears as a `B *` in the caller scope. By saying it's `const` you're promising not to do that. – enobayram Mar 23 '12 at 19:57
  • @ArmenTsirunyan: what if I want to mutate ptr in foo() ? For example, by doing a ptr.reset(new A). Passing const reference will not let me do that. Specifically, I want foo to treat ptr as non-const and still allow polymorphic arguments. Is that possible? – ATemp Mar 23 '12 at 20:01
  • @Atemp: In that case foo must take scoped_ptr&, not A – Armen Tsirunyan Mar 23 '12 at 20:20