Section 16.3 of C++ Primer (5th edition) - Overloading and Templates -, teaches the function matching procedure in the presence of candidate function template(s) instantiati
You're given these declarations:
using std::string;
template <class T> string debug_rep(const T &); /* 1 */
template <class T> string debug_rep(T *); /* 2 */
In the invocation
string s("SO");
debug_rep(&s);
the &s
produces a string*
, which can only match the T const&
of (1) when T
is string*
. For the T*
in (2), there is a match for T
bound to string
. So, provided your quoting is correct, the book is wrong about
debug_rep(const string *&)
being a possible instantiation: there is no such.
The instantiation resulting from T
= string*
would instead be
debug_rep( string* const& )
But which instantiation will be called?
As a general rule the simplest match is superior, but I never manage to remember the exact rules, so, I ask Visual C++ (because its typeid(T).name()
produces readable type names by default):
#include <iostream>
#include <string>
#include <typeinfo>
using namespace std;
template< class T >
struct Type {};
template <class T> auto debug_rep( T const& ) // 1
-> string
{ return string() + "1 --> T = " + typeid(Type<T>).name(); }
template <class T> auto debug_rep( T* ) // 2
-> string
{ return string() + "2 --> T = " + typeid(Type<T>).name(); }
auto main() -> int
{
string s( "SO" );
cout << debug_rep( &s ) << endl;
cout << "The type 'int const' is shown as " << typeid(Type<int const>).name() << endl;
}
And it says:
2 --> T = struct Type<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > The type 'int const' is shown as struct Type<int const >
And so on for your second and third examples: apparently the author got some mixup regarding const
.
EDIT: Thanks to Alf answer, and its elegant trick to conserve complete information about a type while using typeid
, I was able to write a program that addresses most of my questions (changing from std::string
to int
for output readability.)
The complete code can be edited and run rextester online IDE.
Let's define a few classes and methods:
template <class> class Type{}; // Allowing to get full type information with typeid
template <class T> std::string typeStr()
{ return typeid(Type<T>).name(); }
template <class T> void debug_rep(const T &a) /* 1 */
{
std::cout << "[1] T type is: " << typeStr<T>()
<< "\t| arg type is: " << typeStr<decltype(a)>() << std::endl;
}
template <class T> void force_1(const T &a) /* 1 */
{
std::cout << "[forced 1] T type is: " << typeStr<T>()
<< "\t| arg type is: " << typeStr<decltype(a)>() << std::endl;
}
template <class T> void debug_rep(T *a) /* 2 */
{
std::cout << "[2] T type is: " << typeStr<T>()
<< "\t| arg type is: " << typeStr<decltype(a)>() << std::endl;
}
Running:
std::cout << "---First example---" << std::endl;
int i = 41;
debug_rep(&i);
force_1(&i);
Displays:
---First example---
[2] T type is: class Type<int> | arg type is: class Type<int *>
[forced 1] T type is: class Type<int *> | arg type is: class Type<int * const &>
Q1: we can remark that, when we call force_1, instantiating a template corresponding to #1, the argument type is int * const &
, so the book is not correct, and the instantiated candidate #1 would be
debug_rep(int* const &)
Running:
std::cout << "---Second example---" << std::endl;
const int *ip = &i;
debug_rep(ip);
force_1(ip);
Displays:
---Second example---
[2] T type is: class Type<int const > | arg type is: class Type<int const *>
[forced 1] T type is: class Type<int const *> | arg type is: class Type<int const * const &>
Q2.1: Calling force_1
, we remark that the argument type will be int const * const &
, so the book is missing const qualification on the reference. The instantiated candidate will actually be:
debug_rep(const int * const &)
Q2.2 The second candidate being debug_rep(const int *)
, it is an exact match for ip
(which is a pointer to constant integer). To check if the first candidate has a lower rank, let's write:
void debug_rep_plain_b(const int * const &) /* 1 */
{ std::cout << "[plain 1]" << std::endl;}
void debug_rep_plain_b(const int *) /* 2 */
{ std::cout << "[plain 2]" << std::endl; }
If we try to compile:
debug_rep_plain_b(ip)
There is a compilation error for ambiguous call: So the answer to Q2.2 is NO, it is still an exact match ! For the templated version, the compiler actually uses the rule regarding the most specialized template to resolve the ambiguity.
Even if there is a mistake in the deduced candidate, the book is correct regarding the fact that this example illustrates overload resolution using the most specialized template.
Running:
std::cout << "---Third example---" << std::endl;
const int ia[3] = {1, 2, 3};
debug_rep(ia);
force_1(ia);
Displays:
---Third example---
[2] T type is: class Type<int const > | arg type is: class Type<int const *>
[forced 1] T type is: class Type<int const [3]> | arg type is: class Type<int const (&)[3]>
Q3.1 The type deduced for T
by CL is array of const integer, so the book would be mistaken.
BUT the result is inconsistent with GCC or Clang, that would output:
---Third example---
[2] T type is:4TypeIKiE | arg type is: 4TypeIPKiE
[forced 1] T type is:4TypeIA3_iE | arg type is: 4TypeIRA3_KiE
The interesting part being:
[forced 1] T type is:4TypeIA3_iE
meaning that they deduce T
as an array of 3 non-const integers (because _i, not _Ki), which would agree with the book.
I will have to open another question for this one, I cannot understand the type deduction operated by GCC and Clang...