模板参数推导和 cons 限定

san*_*san 5 c++ templates

谁能解释一下为什么代码不能编译。

template<class T, class DER>
struct Base {
  T a;
  Base(const T argB) : a(argB){}
};

template<class T>
struct Derived : Base<T, Derived<T> > {
  Derived(const T argD) : Base<T, Derived<T> >(argD){}
};

int main() {
  int val = 10;
  const int *p = &val;
  /* this was in the original question
  Derived<int*> d(p); // breaks, but compiles with Derived<const int*> d(p);
  */
  Derived d(p); // fails, but Derived<const int*> d(p); compiles
}
Run Code Online (Sandbox Code Playgroud)

错误消息是关于没有从int*到 的转换const int*。正如我所看到的,它T可以被替换int*,在这种情况下,构造函数Derived接收其参数作为 aconst int*并使用 调用基类const int*。那么为什么持续的资格会消失呢?

我显然不明白模板参数推导是如何工作的。我无法找到任何清晰但严格且详尽的描述来说明const*&发挥作用时它是如何工作的。也就是说,a 在这些不同的情况下,类型会被推导成什么。

 Foo(T& a)
 Foo(T  a)
 Foo(T* a)
 Foo(const T a)
 Foo(const T*a)
 Foo(const t&a)
Run Code Online (Sandbox Code Playgroud)

a什么时候

  • 一个东西,
  • 一个指针和
  • 数组。

Joh*_*itb 5

因为 的构造函数DerivedDerived(const T argD),所以在你的情况下它是Derived(int * const)。这不接受const int*.

“ const (指向 int 的指针)”不是“(指向 const int 的指针)”

  • 如果你想限制为 const 指向对象,最好的办法是将构造函数声明为 `Derived(const T* argD)`,并期望类的用户提供 `T` 而不是 `T*` 作为模板参数。 (2认同)

Dav*_*own 2

您的示例中没有模板推导。Derived<int*> d(p);特别将模板参数设置为Tto int*指向 int 的指针)。在本例中,派生构造函数采用一个const T参数,该参数是指向 int 的 const 指针。我认为您的困惑是因为const int* p;没有声明一个指向 int 的 const 指针,而是声明了一个指向 const int 的指针,它不是也不能转换为指向 int 的 const 指针(前者允许您修改指向的值,而后者则没有)。

请记住,C 和 C++ 声明通常是从变量名向外读取,因此从const int* p开始p,向左查看*,然后再向左查看const intp指向 const int 的指针也是如此。是一个关于破译 C 声明的很好的指南,cdecl也是一个非常有用的工具。

您的示例的问题是p指向const int 的指针,但 的 构造函数Derived<int*>采用指向 int 的 const 指针,因为Tis int*。这可能看起来令人困惑,但您可以认为它具有比类型声明const更高的优先级。*因此,在const int *apply consttoint和 then *apply to 整个事物中,生成p一个指向 const int 的指针,而 for 中const T, const 适用于T,这实际上是int*生成const T argD一个argD指向int 的 const 指针

使用同样的想法,您的所有Foo示例都可以轻松破译。

Foo(T& a)       // a is a reference to a value of type T
Foo(T  a)       // a is a value of type T
Foo(T* a)       // a is a pointer to a value of type T
Foo(const T a)  // a is a constant value of type T
Foo(const T* a) // a is a pointer to a constant value of type T
Foo(const T& a) // a is a reference to a constant value of type T
Run Code Online (Sandbox Code Playgroud)

一般来说Foo(T a)Foo(const T a)不能重载,因为对于调用者来说,参数是否被复制到常量变量中并不重要。

更具体地说,if Tis char *(指向 char 的指针)

Foo(char *&a)       // a is a reference to a pointer to a char
Foo(char *a)        // a is a pointer to a char (*)
Foo(char **a)       // a is a pointer to a pointer to a char
Foo(char *const a)  // a is a constant pointer to a char (cannot overload with (*))
Foo(char *const *a) // a is a pointer to a constant pointer to a char
Foo(char *const &a) // a is a reference to a constant pointer to a char
Run Code Online (Sandbox Code Playgroud)

如果Tconst char*(指向 const char 的指针),情况大致相同

Foo(const char *&a)       // a is a reference to a pointer to a const char
Foo(const char *a)       // a is a pointer to a const char (*)
Foo(const char **a)       // a is a pointer to a pointer to a const char
Foo(const char *const a)  // a is a constant pointer to a const char (cannot overload with (*))
Foo(const char *const *a) // a is a pointer to a constant pointer to a const char
Foo(char *const &a) // a is a reference to a constant pointer to a const char
Run Code Online (Sandbox Code Playgroud)

如果Tchar* const(指向字符的 const 指针),那么所有const T重载都是多余的,因为const T相当于TTis 已经是 const 时。

Foo(char *const &a) // a is a reference to a const pointer to a char (+)
Foo(char *const a)  // a is a const pointer to a char (*)
Foo(char *const *a) // a is a pointer to a const pointer to a char (^)
Foo(char *const a)  // a is a const pointer to a char (same as (*))
Foo(char *const *a) // a is a pointer to a const pointer to a char (same as (^))
Foo(char *const &a) // a is a reference to a const pointer to a char (same as (+))
Run Code Online (Sandbox Code Playgroud)