Nested template parameters

I started reading about templates and I found that smart pointers use double templates, something like this:

template <class T>
class myclass
{
public:
   template <class U>
   myclass(U* q) { /* ... */ }
};

      

What is the meaning of this? I know that templated functions output U

because

myclass(new whatever(3));

      

where U

will be whatever*

. So what is it T

? What is the relationship between U

and T

?

I am embarrassed...

+3


source to share


4 answers


The above code example does not establish a relationship between T

and U

.

One type argument is passed to the class template myclass

, the other is the inferred type passed to the constructor.

However, when you find it (perhaps in std::shared_ptr

) it gets more interesting.

Now, in std::shared_ptr

, the body of the constructor imposes the requirement to U

be a descendant of the type from T

. This constructor allows you to create std::shared_ptr<Base>

from Derived*

, knowing inside the constructor what it is being built from Derived*

.

Why do we need this? After all, Derived*

it is possible to convert to Base*

outside the constructor, so why not just take a T*

(aka Base*

)?



Well, a std::shared_ptr<T>

is 3 things put together. This T*

is a control counter and a cleaning function ("Deleter").

When the reference count decreases to 0, the cleanup function is called. By default, the cleanup function calls the object's destructor with a pointer to the object.

But which destructor? Well, the destructor called is type-based U

, not T

. At construction, a destruction function is written that knows the static type U

. This destroy function carries over to all copies of this original shared_ptr<T>

, so even if it is destroyed far away, it still calls ~U

instead ~T

.

If it T

does virtual ~T()

, it doesn't do much (and in fact identical comdat additions or similar methods will cause it to do nothing), but if it has a non-virtual destructor, shared_ptr

name the destructor you want (assuming the type is indeed U

, not some derived type U

) ...

shared_ptr

it is necessary to keep the destroy function for other reasons (you can pass custom destroy functions), so this does not have significant overhead.

+5


source


In your example, myclass

is a template class and constructor myclass::myclass()

is a template method. Both must be of a type in order for them to "work" as expected, where "given" can also mean that the type is inferred.

For example, a valid instance declaration myclass

is



myclass<double> x(new int(3));

      

Here T = double

and U = int

(note what the constructor takes U*

). There should be no relationship between U

and T

.

+1


source


There is no relationship between T

and U

. You can instantiate myclass

with any T

and call the constructor with any argument as long as it's a pointer (in your case):

class A {};
class B {};
myClass<A> x(new B()); // T == A, U == B

      

Note that you cannot specify U

it explicitly , it can only be deduced from the argument.

0


source


Consider a class template:

template<typename T>
class Element
{
    T _element;
public:
    CopyFrom(T t);
};

      

What if you want CopyFrom

any type that is not T

. For example:

Element<int> a;
a.CopyFrom(10.0f);

      

Here is a simple-to-understand int

to float

not be converted, and only this can only be done CopyFrom

(using some auxiliary functions, some other internal overload function, etc.). But to avoid data loss warning. Therefore, you need something like:

a.CopyFrom<float>(10.0f);

      

Here you have specified the type of the argument as float

- now the compiler will be happy. To make it work, you have to make CopyFrom

a function (method) template:

public:
        template<typename U>
        CopyFrom(U t);

      

Now a

has a type Element<int>

, but CopyFrom

will CopyFrom<float>

. Obviosuly, you don't need to use <float>

.

a.CopyFrom/*<float>*/(10.0f); // Element<int>::CopyFrom<float>(...);

      

0


source







All Articles