使用shared_ptr到const T的C ++模板实例化

时间:2011-04-08 19:54:01

标签: c++ const shared-ptr implicit-conversion template-deduction

假设我有一个班级

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) {}
};

以下内容无法编译:

 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);

编译错误是:

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> > >&)'

编译器无法进入std::tr1::shared_ptr<std::vector<float> > 第一个参数std::tr1::shared_ptr<const std::vector<float> >。然而,它可以用于第二个(非模板参数)。

对此的一个解决方案是将调用更改为f(),将其称为f<float>(...) 另一种解决方案是将v1声明为shared_ptr到const vector<float>.

  1. 为什么模板实例化在这里表现得如此不同?
  2. 我对方法的shared_ptr<const T>参数的理解是该方法无法改变shared_ptr所指向的内容。如果我们将shared_ptr更改为原始指针并将v1v2更改为vectors的原始指针,则代码将正常编译。什么是shared_ptr s打破模板扣除?

2 个答案:

答案 0 :(得分:4)

相关类型?

如您所知,T*const T*是相关类型。从第一个到第二个的标准转换(资格转换)。

首先,您必须意识到A<T>A<const T>不是一般相关的类型。这些类型可以具有不同的大小,表示和目的。一个可以定义,但不能定义另一个。

特别是,A<const T>不是const限定A<T>,因此两者之间没有限定转换,而且C ++不够灵活,无法声明用户定义的限定转换。 (用户无法声明任何标准转换,只有用户定义的转化 - 用户定义的转化不是标准转化。)

因此,用户定义的类型与基本类型根本不同:它们与基本类型的方式无关。

shared_ptr<>

shared_ptr<>旨在形成一系列兼容类型:shared_ptr<T>可隐式转换为shared_ptr<U> iff T*可隐式转换为U*。特别是,shared_ptr<T>可隐式转换为shared_ptr<const T>。它也可以隐式转换为shared_ptr<void>,原因相同。

由于shared_ptr<const T>shared_ptr<T>类型没有任何特殊关联,因此从shared_ptr<T>shared_ptr<const T>的转换不会与shared_ptr<T>区分为{{ 1}}。这些只是两种不同的转化,但在任何情境中都不能被视为“首选”,这与从shared_ptr<void>T*(排名=完全匹配)的转换不同,后者优先于{{1}转换} const T*(排名=转化)。

功能模板

在推导出的模板函数参数上允许一些标准转换:

  • 资格转换
  • 一些指针转换:derived-to-base
  • ...

但正如我们所见,T*类型之间不存在此类转换。

这意味着即使允许编译器枚举模板参数的所有可能类型来转换函数模板

void*

成为一组无限的函数原型:

shared_ptr<>

除非你有完全匹配,否则你将无法调用函数:给出声明

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

for every type T, such that shared_ptr<const T> can be instantiated: f (shared_ptr<const T>) 原型集中,有:

struct Base {};
struct Derived : Base {};

shared_ptr<Derived> d;

所以对f的调用不明确,因为这两个候选人都涉及不同的用户定义转换。

答案 1 :(得分:3)

  

为什么模板实例化在这里表现得如此不同?

shared_ptr<T>可通过转换构造函数隐式转换为shared_ptr<const T>。遗憾的是,在模板参数推断期间无法使用此类转换。

为了使参数推断成功,参数的类型必须与参数的类型完全匹配;不使用大多数转化,包括用户定义的转化。

只允许几个非常基本的转换,包括数组到指针的衰减和函数到函数指针的转换(因为没有函数参数可以是数组类型或函数类型,这些转换不会导致混淆)。此外,顶级const和volatile限定符完全被忽略。