谁能解释一下为什么代码不能编译。
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什么时候
因为 的构造函数Derived是Derived(const T argD),所以在你的情况下它是Derived(int * const)。这不接受const int*.
“ const (指向 int 的指针)”不是“(指向 const int 的指针)”。
您的示例中没有模板推导。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 int,p指向 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)
如果T是const 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)
如果T是char* const(指向字符的 const 指针),那么所有const T重载都是多余的,因为const T相当于T当Tis 已经是 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)