Search code examples
c++constantsshared-ptrimplicit-conversiontemplate-argument-deduction

C++ template instantiation with shared_ptr to const T


Suppose I have a class

template <typename T>
class A {
 public:
  template <typename V>
    void f(std::tr1::shared_ptr<const std::vector<V> > v1, 
           std::tr1::shared_ptr<const std::vector<float> > v2) {}
};

The following does not compile:

 A<string> a;
  std::tr1::shared_ptr<std::vector<float> > v1(new std::vector<float>());
  std::tr1::shared_ptr<std::vector<float> > v2(new std::vector<float>());
  a.f(v1, v2);

The compiler error is:

error: no matching function for call to 'A<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >::f(std::tr1::shared_ptr<std::vector<float, std::allocator<float> > >&, std::tr1::shared_ptr<std::vector<float, std::allocator<float> > >&)'

The compiler could not make std::tr1::shared_ptr<std::vector<float> > into std::tr1::shared_ptr<const std::vector<float> > for the first argument. Yet it could for the second (non-template argument).

One solution to this is to change the call to f(), call it like this f<float>(...).
Another solution is to declare v1 as shared_ptr to const vector<float>.

  1. Why is template instantiation behaving so differently here?
  2. My understanding of having a shared_ptr<const T> as argument to a method is that the method cannot change what the shared_ptr is pointing to. If we change the shared_ptrs to raw pointers and v1, v2 as raw pointers to vectors, then the code will compile fine. What is it about shared_ptrs that breaks template deduction?

Solution

  • Why is template instantiation behaving so differently here?

    A shared_ptr<T> is implicitly convertible to a shared_ptr<const T> via a converting constructor. Unfortunately, such conversions cannot be used during template argument deduction.

    In order for argument deduction to succeed, the type of the argument has to match the type of the parameter exactly; most conversions, including user-defined conversions, are not used.

    Only a few very basic conversions are allowed, including array-to-pointer decay and function-to-function-pointer conversion (since no function parameter can be of array type or function type, these conversions cannot lead to confusion). In addition, top-level const- and volatile- qualifiers are wholly ignored.