1

I got compile error when passing shared_ptr<Derived>& as shared_ptr<Base>&, see the below code and detailed question.

Note: this question is similar to "Passing shared_ptr<Derived> as shared_ptr<Base>" but not duplicate.

#include <memory>
class TBase
{
public:
  virtual ~TBase() {}
};
class TDerived : public TBase
{
public:
  virtual ~TDerived() {}
};
void FooRef(std::shared_ptr<TBase>& b)
{
  // Do something
}

void FooConstRef(const std::shared_ptr<TBase>& b)
{
  // Do something
}

void FooSharePtr(std::shared_ptr<TBase> b)
{
  // Do something
}
int main()
{
  std::shared_ptr<TDerived> d;
  FooRef(d);  // *1 Error: invalid initialization of reference of type ‘std::shared_ptr<TBase>&’ from expression of type ‘std::shared_ptr<TDerived>’
  FooConstRef(d); // *2 OK, just pass by const reference
  FooSharePtr(d); // *3 OK, construct a new shared_ptr<>
  return 0;
}

Compiled by g++ -std=c++11 -o shared_ptr_pass_by_ref shared_ptr_pass_by_ref.cpp

Env: Ubuntu 14.04, g++ (Ubuntu 4.8.2-19ubuntu1) 4.8.2

Detailed question: Why is it OK to pass by const reference (*2), but not OK to pass by reference (*1)?

Note: I know the best practice is to pass by const reference, but just want to know why the compile error occurs.

Community
  • 1
  • 1
Mine
  • 4,123
  • 1
  • 25
  • 46
  • 1
    What if `FooRef` does `b.reset(new TBase)`? If the call were possible, you would end up with `std::shared_ptr` holding a `TBase*`. By the way, I suspect that `FooConstRef` call constructs a temporary that then binds to const reference; but temporaries can't bind to non-const ref. – Igor Tandetnik Sep 19 '14 at 02:57

1 Answers1

4

You seem to expect some kind of template covariance, whereby AnyTemplateClass<Derived> can bind to AnyTemplateClass<Base>&. Templates don't work this way. Generally, AnyTemplateClass<Derived> and AnyTemplateClass<Base> are two distinct, completely unrelated classes.

A specific template class may, or course, provide a relationship in some form. shared_ptr<T> in particular has a templated constructor accepting shared_ptr<U> for any U such that U* is convertible to T*.

FooConstRef(d) call works by constructing a temporary - effectively

shared_ptr<TBase> temp(d);
FooConstRef(temp);

But temporaries can't bind to non-const references, that's why FooRef(d) doesn't work in a similar way.

Igor Tandetnik
  • 50,461
  • 4
  • 56
  • 85
  • Great answer! But one more question, why the temporary of `shared_ptr` can be created **effectively**? As long as it's a new `shared_ptr`, it still increase the `use_count`. Then is there any performance difference between `FooConstRef()` and `FooSharePtr()`, if we always pass shared_ptr? – Mine Sep 19 '14 at 03:18
  • "effectively" != "efficiently". In light of this fact, I'm not sure I quite understand your question. You seem to assume some meaning in my statement that I didn't place there. – Igor Tandetnik Sep 19 '14 at 05:29
  • Sorry for the unclear question, let me re-phrase: is there any performance difference between `FooConstRef()` and `FooSharePtr()`, if the passed parameter is `shared_ptr`? If I understand correctly, they have the same behavior, because both construct a temporary `shared_ptr`, right? – Mine Sep 19 '14 at 07:55
  • No, I don't believe there's any difference, under given constraints. – Igor Tandetnik Sep 19 '14 at 13:39