Creating a C ++ instance from 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>()); af(v1, v2); 

Compiler Error:

 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> > in std::tr1::shared_ptr<const std::vector<float> > for the first argument. However, this may be for the second (argument without a pattern).

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

Question 1) Why is this template instance behavior created here?

Question 2) My understanding of having the shared_ptr argument as a method is that the method cannot change what shared_ptr points to. If we change shared_ptr to raw pointers and v1, v2 as source pointers to vectors, then the code will compile fine. What is it about shared_ptrs that breaks the residue of a pattern?

+4
source share
2 answers

Why is creating a template here so different?

A shared_ptr<T> implicitly converted to shared_ptr<const T> through the conversion constructor. Unfortunately, such transformations cannot be used in the derivation of the template argument.

For the deduction argument to succeed, the type of the argument must exactly match the type of the parameter; most conversions, including custom conversions, are not used.

Only a few very simple transformations are allowed, including the conversion of matrices to a pointer and the conversion of a function-to-function-pointer (since no function parameter can be an array type or a function type, these conversions cannot lead to confusion). In addition, categorical and volatile top-level qualifiers are completely ignored.

+3
source

Related types?

As you know, T* and const T* are related types. There is a standard conversion from first to second (qualification conversion).

First, you must understand that A<T> and A<const T> not common types. These types can have different sizes, views and purposes. You can define, but not another.

In particular, A<const T> not a constant A<T> , so there is no qualification conversion between them, and C ++ does not declare user-friendly qualification conversions flexibly. (A user cannot declare a standard transformation, only a user-defined transformation โ€” a user-defined transformation is not a standard transformation.)

Thus, user-defined types are fundamentally different from the basic types: they cannot be related to how the basic types exist.

shared_ptr<>

shared_ptr<> intended to create a family of compatible types: shared_ptr<T> implicitly converted to shared_ptr<U> if if T* implicitly converted to U* . In particular, shared_ptr<T> implicitly converted to shared_ptr<const T> . It is also implicitly converted to shared_ptr<void> , so for the same reason.

Since the types shared_ptr<const T> and shared_ptr<T> not connected in any special way, the conversion from shared_ptr<T> to shared_ptr<const T> does not differ from shared_ptr<T> to shared_ptr<void> . These are just two different conversions, but none of them can be considered โ€œpreferredโ€ in any context, unlike the conversion from T* to const T* (rank = exact match), which is preferable to converting from T* to void* (rank = conversion).

Function Templates

Some standard conversions for argument function arguments are allowed:

  • qualification transformations
  • some pointer conversions: derivatives to the base
  • ...

But, as we have seen, such transformations do not exist between the shared_ptr<> types.

This means that even if the compiler was allowed to list all possible types for the template parameter in order to include the function template

 template <typename V> void f (shared_ptr<const T> v1); 

into an endless set of function prototypes:

 for every type T, such that shared_ptr<const T> can be instantiated: f (shared_ptr<const T>) 

if you donโ€™t have an exact match, you wonโ€™t be able to call the function: given the ads

 struct Base {}; struct Derived : Base {}; shared_ptr<Derived> d; 

Among the many prototypes f :

 f (shared_ptr<const Base>) f (shared_ptr<const Derived>) 

therefore, calling f (d) will be ambiguous, since both of these candidates are associated with different user transformations.

+4
source

Source: https://habr.com/ru/post/1347303/


All Articles