There is no template deduction in your example. Derived<int*> d(p);
specifically sets the template parameter to T
to int*
(pointer to int). Your derived constructor takes a const T
parameter which is a const pointer to int in this case. I think your confusion is because const 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* p
you start at p
, go left and see *
and then go farther left and see const int
, so p
is 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 p
is a pointer to const int, but the constructor of Derived<int*>
takes a const pointer to int since T
is int*
. This may seem confusing, but you can think of const
as having a higher precedence than *
in type declarations. So in const int *
the const
applies to int
and then *
applies to the whole thing making p
a pointer to const int whereas for const T
, the const applies to T
, which is actually int*
so const T argD
makes argD
a const pointer to int.
Using this same idea all your Foo
examples can be easily deciphered.
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
In general only Foo(T a)
and Foo(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 T
is char *
(pointer to 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
If T
is const char*
(pointer to a const char) things are much the same
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
If T
is char* const
(const pointer to a char) then all the const T
overloads are redundant because const T
is equivalent to T
when T
is already 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 (+))