我开始阅读模板,我发现智能指针使用双模板,如下所示:
template <class T>
class myclass
{
public:
template <class U>
myclass(U* q) { /* ... */ }
};
那是什么意思?我知道模板化函数会推导U
,如
myclass(new whatever(3));
其中U
为whatever*
。那么T
是什么? U
和T
之间的关系是什么?
我很困惑......
答案 0 :(得分:5)
以上示例代码与T
和U
之间没有任何关系。
一个是传递给类模板myclass
的类型参数,另一个是传递给构造函数的推导类型。
然而,你找到它的地方(可能在std::shared_ptr
中)会变得更有趣。
现在,在std::shared_ptr
中,构造函数的主体强制要求U
是来自T
的后代类型。该构造函数允许您从std::shared_ptr<Base>
创建Derived*
,同时在构造函数中知道它是从Derived*
构造的。
我们为什么要这样?毕竟,Derived*
可以在构造函数之外转换为Base*
,那么为什么不采用T*
(又名Base*
)?
嗯,std::shared_ptr<T>
是捆绑在一起的3件事。它是T*
,参考计数器和清理功能(“删除器”)。
当引用计数减少为0时,将调用清除函数。默认情况下,cleanup函数调用指向对象的析构函数。
但是哪个析构函数?好吧,调用的析构函数基于类型U
,不 T
。在构造时,编写一个知道静态类型U
的销毁函数。此销毁功能会被转移到原始shared_ptr<T>
的所有副本,因此即使它被远处销毁,它仍然会调用~U
而不是~T
。
如果T
有一个virtual ~T()
,这没什么用(事实上,相同的comdat折叠或类似的技术会使它什么都不做),但是如果它有一个非虚拟的析构函数, shared_ptr
将调用正确的析构函数(假设类型实际为U
而不是某些派生类型的U
)。
shared_ptr
需要存储销毁函数以用于其他原因(您可以传递自定义销毁函数),因此这不会产生很大的开销。
答案 1 :(得分:1)
在您的示例中,myclass
是模板类,构造函数myclass::myclass()
是模板方法。两者都必须有一个类型,以便他们可以&#34;工作&#34;正确地,在哪里&#34;给予&#34;也可能意味着推断出类型。
例如myclass
实例的有效声明是
myclass<double> x(new int(3));
此处T = double
和U = int
(请注意构造函数采用U*
)。不需要在U
和T
之间建立关系。
答案 2 :(得分:0)
T
和U
之间没有任何关系。您可以使用任何myclass
实例化T
并使用任何参数调用构造函数,只要它是指针(在您的情况下):
class A {};
class B {};
myClass<A> x(new B()); // T == A, U == B
请注意,您无法明确指定U
,只能从参数中推断出来。
答案 3 :(得分:0)
考虑一个类模板:
template<typename T>
class Element
{
T _element;
public:
CopyFrom(T t);
};
如果您想CopyFrom
不 T
的任何类型,该怎么办?例如:
Element<int> a;
a.CopyFrom(10.0f);
此处,仅仅为了理解int
到float
转换是不可能的,只有CopyFrom
可以做到(使用一些辅助函数,一些其他内部重载函数等) - 但是避免任何数据丢失警告。因此,您需要以下内容:
a.CopyFrom<float>(10.0f);
在这里,您将参数类型指定为float
- 编译器现在会很高兴。为了使其有效,您需要使CopyFrom
成为一个函数(方法)模板:
public:
template<typename U>
CopyFrom(U t);
现在,a
的类型为Element<int>
,但CopyFrom
为CopyFrom<float>
。显然,您不需要使用<float>
。
a.CopyFrom/*<float>*/(10.0f); // Element<int>::CopyFrom<float>(...);