4

假设我有一堂课

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>方法的 as 参数的理解是,该方法不能改变shared_ptr指向的内容。如果我们将shared_ptrs 更改为原始指针和v1v2作为指向 的原始指针vectors,那么代码将编译得很好。shared_ptr打破模板推导的 s 是什么?
4

2 回答 2

4

相关类型?

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

首先,您必须意识到这一点,A<T>并且A<const T>通常不是相关类型。这些类型可能有不同的大小、表示和用途。可以定义一个,但不能定义另一个。

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

因此,用户定义类型与基本类型根本不同:它们不能像基本类型那样相关。

shared_ptr<>

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

因为类型shared_ptr<const T>shared_ptr<T>没有以任何特殊方式相关,所以从 to 的转换与toshared_ptr<T>没有shared_ptr<const T>区别。这只是两种不同的转换,但在任何情况下都不能被视为“首选”,这与从to (rank = Exact match) 的转换相比从to (rank = Conversion) 的转换更受欢迎。shared_ptr<T>shared_ptr<void>T*const T*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原型中,有:

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

因此调用f (d)将是模棱两可的,因为这两个候选者都涉及不同的用户定义的转换。

于 2011-12-15T04:06:14.017 回答
3

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

A可以通过转换构造函数shared_ptr<T>隐式转换为 a 。shared_ptr<const T>不幸的是,在模板参数推导过程中不能使用这种转换。

为了使参数推导成功,参数的类型必须与参数的类型完全匹配;大多数转换,包括用户定义的转换,都没有被使用。

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

于 2011-04-08T20:09:45.393 回答