Could anyone explain why the code does not compile.
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
}
The error message is that about no conversion from int* to const int*. As I see it T can be substitues by int* and in that case the constructor to Derived receives its argument as a const int* and invokes the base with const int*. Why then is the constant qulaification getting lost.
I clearly do not understand how template argument deduction works. I have not been able to find any lucid but rigorous and exhaustive description of how it works when const, * and & are in play. That is, what will a get type deduced to in these various cases.
Foo(T& a)
Foo(T a)
Foo(T* a)
Foo(const T a)
Foo(const T*a)
Foo(const t&a)
when a is
- an object,
- a pointer and
- an array.
There is no template deduction in your example.
Derived<int*> d(p);specifically sets the template parameter toTtoint*(pointer to int). Your derived constructor takes aconst Tparameter which is a const pointer to int in this case. I think your confusion is becauseconst int* p;does not declare a const pointer to int, but instead declares a pointer to const int which is not, and cannot be converted to, a const pointer to int (the former lets you modify the pointed to value while the latter does not).Remember that C and C++ declarations are generally read from the variable name outwards, so for
const int* pyou start atp, go left and see*and then go farther left and seeconst int, sopis a pointer to a const int. Here is a good guide on deciphering C declarations and cdecl is also a very useful tool.The problem with your example is
pis a pointer to const int, but the constructor ofDerived<int*>takes a const pointer to int sinceTisint*. This may seem confusing, but you can think ofconstas having a higher precedence than*in type declarations. So inconst int *theconstapplies tointand then*applies to the whole thing makingpa pointer to const int whereas forconst T, the const applies toT, which is actuallyint*soconst T argDmakesargDa const pointer to int.Using this same idea all your
Fooexamples can be easily deciphered.In general only
Foo(T a)andFoo(const T a)cannot be overloaded because it doesn’t matter to the caller whether the argument is copied into a constant variable or not.More specifically, if
Tischar *(pointer to char)If
Tisconst char*(pointer to a const char) things are much the sameIf
Tischar* const(const pointer to a char) then all theconst Toverloads are redundant becauseconst Tis equivalent toTwhenTis already const.