模板参数推导和const限定

时间:2012-12-01 01:31:12

标签: 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
}

错误消息是关于从int*const int*的转换。正如我所看到的,T可以是int*的替代品,在这种情况下,Derived的构造函数将其参数作为const 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)
a

  • 一个对象,
  • 指针和
  • 一个阵列。

2 个答案:

答案 0 :(得分:4)

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

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

答案 1 :(得分:1)

您的示例中没有模板推断。 Derived<int*> d(p);专门将模板参数设置为Tint*指向int 的指针)。在这种情况下,派生的构造函数采用const T参数,该参数是 const指向int 的指针。我认为你的困惑是因为const int* p;没有声明一个指向int 的 const指针,而是声明一个指向const int 的指针,它不是,并且不能被转换to,一个 const指向int 的指针(前者允许你修改指向的值,而后者则没有)。

请记住,C和C ++声明通常从变量名称向外读取,因此const int* pp开始,向左走,看*然后向左走,看看const int,所以p是指向const int的指针。 Here是解密C声明的好指南,cdecl也是一个非常有用的工具。

您的示例的问题是p指向const int 的指针,但Derived<int*>的构造函数将 const指针指向int 因为Tint*。这可能看起来令人困惑,但您可以将const视为在类型声明中具有比*更高的优先级。因此,在const int *中,const适用于int,然后*适用于整个事情,使p成为指向const int的指针而对于const T,const适用于T,实际上int*因此const T argD使argD成为 const指针指向int

使用同样的想法,您的所有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

通常只有Foo(T a)Foo(const T a)不能重载,因为调用者是否将参数复制到常量变量中并不重要。

更具体地说,如果Tchar *(指向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

如果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

如果Tchar* const(指向char的const指针),则所有const T重载都是多余的,因为const T等同于T T 1}}已经是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 (+))