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

发布于 2024-10-31 06:33:50 字数 1535 浏览 6 评论 0 原文

假设我有一个类

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::tr1::shared_ptr > 对于第一个参数。但它可以用于第二个(非模板参数)。

解决此问题的一种方法是更改​​对 f() 的调用,像这样调用 f(...)
另一个解决方案是将 v1 声明为 const vector 的 shared_ptr。

  1. 为什么模板实例化的行为在这里如此不同?
  2. 我对将 shared_ptr 作为方法参数的理解是,该方法不能更改 shared_ptr 所指向的内容。如果我们将 shared_ptr 更改为原始指针,将 v1v2 更改为指向 vector 的原始指针,则代码会编译得很好。 shared_ptr 是什么破坏了模板推导?

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>());
  a.f(v1, v2);

The compiler error is:

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> > into
std::tr1::shared_ptr<const std::vector<float> > for the first argument. Yet it could for the second (non-template argument).

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

  1. Why is template instantiation behaving so differently here?
  2. My understanding of having a shared_ptr<const T> as argument to a method is that the method cannot change what the shared_ptr is pointing to. If we change the shared_ptrs to raw pointers and v1, v2 as raw pointers to vectors, then the code will compile fine. What is it about shared_ptrs that breaks template deduction?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

浪漫之都 2024-11-07 06:33:50

相关类型?

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

首先,您必须意识到 AA 不是一般相关类型。这些类型可能有不同的大小、表示形式和用途。可以定义其中之一,但不能定义另一个。

特别是,A 不是 const 限定的 A,因此两者之间不存在限定转换,并且 C++ 不够灵活声明的用户定义的限定转换。 (用户不能声明任何标准转换,只能声明用户定义的转换 - 用户定义的转换不是标准转换。)

因此用户定义的类型与基本类型有根本的不同:​​它们不能以这种方式关联基本类型是。

shared_ptr<>

shared_ptr<> 旨在形成一系列兼容类型:shared_ptr 可以隐式转换为 shared_ptr 当且仅当 T* 可以隐式转换为 U*。特别是,shared_ptr 可隐式转换为shared_ptr。它也可以隐式转换为 shared_ptr,所以道理是一样的。

由于类型 shared_ptrshared_ptr 没有以任何特殊方式相关,因此从 shared_ptr 的转换code>shared_ptrshared_ptr 没有区别shared_ptr。这些只是两种不同的转换,但在任何上下文中都不能被视为“首选”,这与从 T*const T* 的转换(rank = 完全匹配)不同。优于从 T*void* 的转换(排名 = Conversion)。

函数模板

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

  • 限定转换
  • 一些指针转换:派生到基
  • ...

但是正如我们所见,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) 的调用将是不明确的,因为这两个候选者都涉及不同的用户定义转换。

Related types?

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

First, you have to realise that A<T> and A<const T> are not in general related types. These type could have different size, representation, and purpose. One could be defined but not the other.

In particular, A<const T> is not a const qualified A<T>, so there is no qualification-conversion between the two, and C++ is not flexible enough to declared user-defined qualification-conversions. (A user cannot declare any standard conversion, only user-defined conversion - user-defined conversion are not standard conversions.)

So user-defined types are fundamentally different from fundamental types: they cannot be related the way fundamental types are.

shared_ptr<>

shared_ptr<> is designed to form a family of compatible types: shared_ptr<T> is implicitly convertible to shared_ptr<U> iff T* is implicitly convertible to U*. In particular, shared_ptr<T> is implicitly convertible to shared_ptr<const T>. It is also implicitly convertible to shared_ptr<void>, so the same reason.

Because types shared_ptr<const T> and shared_ptr<T> are not related in any special way, the conversion from shared_ptr<T> to shared_ptr<const T> is not distinguished from shared_ptr<T> to shared_ptr<void>. These are just two different conversions, but neither can be considered "preferred" in any context, unlike the conversion from T* to const T* (rank = Exact match) which is preferred over conversion from T* to void* (rank = Conversion).

Function templates

Some standard conversions are allowed on deduced templates function arguments:

  • qualification conversions
  • some pointer conversions: derived-to-base
  • ...

But as we have seen, no such conversions exist between shared_ptr<> types.

This means that even if the compiler was allowed to enumerate all possible types for a template parameter to turn a function template

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

into an infinite set of functions prototypes:

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

unless you have an exact match, you would not be able to call a function: given the declarations

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

shared_ptr<Derived> d;

among the set of f prototypes, there is:

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

so a call to f (d) would be ambiguous as both of these candidates involve different user-defined conversions.

旧城烟雨 2024-11-07 06:33:50

为什么模板实例化的行为如此不同?

shared_ptr 可通过转换构造函数隐式转换为 shared_ptr。不幸的是,这样的转换不能在模板参数推导过程中使用。

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

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

Why is template instantiation behaving so differently here?

A shared_ptr<T> is implicitly convertible to a shared_ptr<const T> via a converting constructor. Unfortunately, such conversions cannot be used during template argument deduction.

In order for argument deduction to succeed, the type of the argument has to match the type of the parameter exactly; most conversions, including user-defined conversions, are not used.

Only a few very basic conversions are allowed, including array-to-pointer decay and function-to-function-pointer conversion (since no function parameter can be of array type or function type, these conversions cannot lead to confusion). In addition, top-level const- and volatile- qualifiers are wholly ignored.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文