overload with template function

I am a new beginner,got confused about:

1
2
3
4
template<typename T> int compare(T &a,T &b);
int compare(const char *a,const char *b);
char ch_arr1[6]="world",ch_arr2[6]="hello";
compare(ch_arr1,ch_arr2);


After running the code above,we got to know the non-template function is called.
What I know is that the array aguments ch_arr1,ch_arr2 will not be converted to char * because the parameters are references in the template functions.
ch_arr1,ch_arr2 need to be converted to const char * if compare(const char *a,const char *b) were called.
I just wanna know what exactly happened behind that? and why?
Thank you!
> After running the code above,we got to know the non-template function is called.

No. The template will be instantiated and called.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>

template<typename T> int compare( T&, T& )
{ std::cout << "template\n" ; return 0 ; }

int compare( const char*, const char* )
{ std::cout << "non-template overload\n" ; return 0 ; }

template<typename T> int compare2( const T&, const T& )
{ std::cout << "template const\n" ; return 0 ; }

int compare2( const char*, const char* )
{ std::cout << "non-template overload\n" ; return 0 ; }

int main()
{
    char ch_arr1[6] = "world", ch_arr2[6] = "hello" ;

    compare( ch_arr1, ch_arr2 ) ; // template; T decuced to be char[6]
    compare2( ch_arr1, ch_arr2 ) ; // template const; T decuced to be char[6]

    compare( ch_arr1, +ch_arr2 ) ; // non-template overload
    compare2( ch_arr1, +ch_arr2 ) ; // non-template overload

    char ch_arr3[7] = "hello";

    compare( ch_arr1, ch_arr3 ) ; // non-template overload
    compare2( ch_arr1, ch_arr3 ) ; // non-template overload

    const char ch_arr4[6] = "world" ;

    compare( ch_arr1, ch_arr4 ) ; // non-template overload
    compare2( ch_arr1, ch_arr4 ) ; // template const; T decuced to be char[6]
}

http://coliru.stacked-crooked.com/a/3b5cbcf5ad01887b
Why does unary + operator prevent the function templates from being used?
The unary + operator can't be applied to arrays; it can be applied to pointers. So an implicit array to pointer conversion is applied, and the unary + operates on the result of the conversion.
what principle can be used to decide which function will be called?
1
2
3
4
5
6
7
8
9
template<typename T> int compare(T &a,T &b);

int compare(const char *a,const char *b);

void foo()
{
    char ch_arr1[6]="world",ch_arr2[6]="hello";
    compare(ch_arr1,ch_arr2);
}


A parameterized function can be overloaded as in our example - ie. it has the same name as a nonparameterized function. And when the function is invoked, the call can be resolved either to an instantiation of the function template or to the non-template function. Normal function overload resolution process is used to determine which function best matches the arguments on the call.

Step 1.
-------
Identify the set of candidate functions - the set of the functions that have the same name as the called function, and for which a declaration is visible at the point of the call.

The non-template function is an obvious addition to the candidate list. An instantiation of the function template (the template function can be instantiated using the function call arguments) is also treated as a candidate function.

Step 2.
-------
Select the set of viable functions that can be called from this set of candidate functions. For a candidate function to qualify as a viable function, type conversions must exist to convert each actual argument type to the type of the corresponding formal parameter.

In our example, both candidate functions can be called and are viable.

Step 3.
-------
Rank the type conversions to be applied to the arguments and use the rule of intersection to select the 'best' function from the set of viable functions. However, if both a non-template function and an instantiation of a template function are equally 'best' matched, the non-template function is given precedence over the template instantiation.

In our example, compare(ch_arr1,ch_arr2); type of the arguments is char[6] (array of 6 char). The template instantiation gives a precise match while the non-template function requires an array-to-pointer conversion. The overload therefore resolves to the template instantiation.
Last edited on
However, if both a non-template function and an instantiation of a template function are equally 'best' matched, the non-template function is given precedence over the template instantiation.


For completeness...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>

template<typename T> int compare( T&, T& )
{ std::cout << "template\n" ; return 0 ; }

int compare( char [6], char [6] )
{ std::cout << "non-template overload\n" ; return 0 ; }

int main()
{
    char ch_arr1[6] = "world", ch_arr2[6] = "hello" ;

    compare( ch_arr1, ch_arr2 ) ;
}
non-template overload
compare2( ch_arr1, ch_arr2 ) ;
Do that count as char[6] to const char (&)[6] conversion?or that was also regarded as precise match?

template<typename T> int compare2( const T&, const T& ),that const is so confusing.
int compare( char [6], char [6] ); is identical to int compare( char* , char* );

No conversions required, lvalue-to-rvalue conversion, array-to-pointer conversion, function-to-pointer conversion and (top-level) qualification conversion all rank as 'exact match' (best).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>

template<typename T> int compare( T&, T& )
{ std::cout << "template\n" ; return 0 ; }

int compare( char*, char* )
{ std::cout << "non-template overload\n" ; return 0 ; }

int main()
{
    char ch_arr1[6] = "world", ch_arr2[6] = "hello" ;

    compare( ch_arr1, ch_arr2 ) ; // non-template overload

    // candidate 1 : int compare( char*, char* ), viable, rank: exact match

    // candidate 2 : instantiation of template int compare( T&, T& ) with T == char[6]
    //               int compare( char (&)[6], char (&)[6] ), viable, rank: exact match

    //  the non-template function is given precedence over the template instantiation
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>

template<typename T> int compare( T&, T& )
{ std::cout << "template\n" ; return 0 ; }

int compare( const char*, const char* )
{ std::cout << "non-template overload\n" ; return 0 ; }

int main()
{
    char ch_arr1[6] = "world", ch_arr2[6] = "hello" ;

    compare( ch_arr1, ch_arr2 ) ; // template

    // candidate 1 : int compare( const char*, const char* ), viable,
    //               requires pointer conversion (char* to const char*),
    //               rank: standard conversion (ranks below exact match and promotion)

    // candidate 2 : instantiation of template int compare( T&, T& ) with T == char[6]
    //               int compare( char (&)[6], char (&)[6] ), viable, exact match

    //  the insstantiation of the template function is the best match
}



> template<typename T> int compare2( const T&, const T& ), that const is so confusing.

When instantiated with compare( ch_arr1, ch_arr2 ) ;, the instantiation is equivalent to:
1
2
3
4
using TYPE = char[6] ; // TYPE is a type alias for array of 6 char
using ARGTYPE = const TYPE& ; // ARGTYPE is a reference to const TYPE
                      // the const here maps to reference to array of 6 const char
int compare2( ARGTYPE, ARGTYPE ) ;


That is:
1
2
3
4
5
6
7
8
using array_type = char[6] ;
using array_type2 = const char[6] ;
void foo( array_type ) {} // foo( char* )
void foo( array_type2 ) {} // foo( const char* )
void foo( array_type& ) {} // foo( char (&)[6] )
void foo( array_type2& ) {} // foo( const char (&)[6] )
// void foo( const array_type& ) {} // *** error, redefinition of foo( const char (&)[6] )
// void foo( const array_type2& ) {} // *** error, redefinition of foo( const char (&)[6] ) 
Last edited on
Let's sum it up :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>

template<typename T> int compare( T&, T& )
{ std::cout << "template\n" ; return 0 ; }

int compare( const char*, const char* )
{ std::cout << "non-template overload\n" ; return 0 ; }

template<typename T> int compare2( const T&, const T& )
{ std::cout << "template const\n" ; return 0 ; }

int compare2( const char*, const char* )
{ std::cout << "non-template overload\n" ; return 0 ; }

int main()
{
    char ch_arr1[6] = "world", ch_arr2[6] = "hello" ;

    compare( ch_arr1, ch_arr2 ) ; // template; T decuced to be char[6]
    compare2( ch_arr1, ch_arr2 ) ; // template const; T decuced to be char[6]

    compare( ch_arr1, +ch_arr2 ) ; // non-template overload
    compare2( ch_arr1, +ch_arr2 ) ; // non-template overload

    char ch_arr3[7] = "hello";

    compare( ch_arr1, ch_arr3 ) ; // non-template overload
    compare2( ch_arr1, ch_arr3 ) ; // non-template overload

    const char ch_arr4[6] = "world" ;

    compare( ch_arr1, ch_arr4 ) ; // non-template overload
    compare2( ch_arr1, ch_arr4 ) ; // template const; T decuced to be char[6]
}

1
2
3
4
5
6
 compare2( ch_arr1, ch_arr2 ) ; // template overload
// candidate 1 : int compare2(const char*,const char* ), viable, rank:  requires pointer conversion (char* to const char*)

// candidate 2 : instantiation of template int compare2(const T&,const T& ) with T == char[6]
//               int compare2(const char (&)[6], const char (&)[6] ), viable, rank: exact match


1
2
3
 compare( ch_arr1, +ch_arr2 ) ; // non-template overload
compare2( ch_arr1, +ch_arr2 ) ; // non-template overload
//T bound to char [6] and bound to char *,contradiction,so non-template overload 


if change that to:
1
2
3
4
5
6
7
8
 
    compare2( +ch_arr1, +ch_arr2 ) ; // template overload
// candidate 1 : int compare2( const char*, const char* ), viable,
    //               requires pointer conversion (char* to const char*),
    //               rank: standard conversion (ranks below exact match and promotion)

    // candidate 2 : instantiation of template int compare2(const T&, const T& ) with T == char *
    //               int compare2(const char *&,const char *& ), viable, exact match 


And ...
1
2
3
compare( ch_arr1, ch_arr3 ) ; // non-template overload
compare2( ch_arr1, ch_arr3 ) ; // non-template overload
//T bound to char [6] and bound to char [7],contradiction,so non-template overload 


1
2
3
compare( ch_arr1, ch_arr4 ) ; // non-template overload
//  instantiation of template int compare( T&, T& ) with T == char[6],
//int compare( char (&)[6], char (&)[6] ) can't accept argument of const char [6],so non-template overload 



1
2
3
4
5
6
7
compare2( ch_arr1, ch_arr4 ) ; // template const; T decuced to be char[6]
// candidate 1 : int compare2( const char*, const char* ), viable,
    //               requires pointer conversion (char* to const char*),
    //               rank: standard conversion (ranks below exact match and promotion)

    // candidate 2 : instantiation of template int compare2(const T&, const T& ) with T == char *
    //               int compare2(const char *&,const char *& ), viable, exact match  


Is that right?
When we have
1
2
template<typename T> int compare2( const T&, const T& )
{ std::cout << "template const\n" ; return 0 ; }

The const in const T& is a top-level const that applies to T

1
2
3
4
compare2( +ch_arr1, +ch_arr2 ) ;

// candidate 2 : instantiation of template int compare2(const T&, const T& ) with T == char *
// int compare2( char* const&, char*const & ), viable, exact match  


The deduced T is char*; const T& is char* const&

http://www.parashift.com/c++-faq-lite/const-ptr-vs-ptr-const.html
As what you said,const char *& is a reference to const char *, and char* const& is a const reference to char *.
const char* & - reference to pointer to const char
char* const & - reference to const pointer to char
const char* const & - reference to const pointer to const char
char* & - reference to pointer to char.

There is nothing like a 'const reference'; references can't be rebound anyway. References can't be qualified for the same reason that they do not have an address; references are not objects, they are aliases.

What we can have is a 'reference to const' - a reference can refer to a const-qualified object.
Very impressive. Really nice of you. Thank you!
Actually I have been learning C++ for months, but didn't do well on that. So any suggestions you can offer?I mean,any learning materials, any learning methods, any learning experience. :)
Books:

'Accelerated C++' by Koenig and Moo
http://www.amazon.com/Accelerated-C-Practical-Programming-Example/dp/020170353X

'The C++ Standard Library: A Tutorial and Reference' (Second Edition) by Josuttis
http://www.amazon.com/The-Standard-Library-Tutorial-Reference/dp/0321623215

And perhaps, at a later time:
'Introduction to the Boost C++ Libraries' Volumes I and II by Demming
http://www.amazon.com/Introduction-Boost-Libraries-Volume-Foundations/dp/9491028014
http://www.amazon.com/Introduction-Boost-Libraries-Volume-II/dp/9491028022

It would help if you try to be active in these forums. Just lurk around, assist others where you can, ask for clarifications when you need something to be explained etc. Often, the very act of explaining something, from first principles, to another persons helps you to crystallize your own concepts.
Really appreciate.
Topic archived. No new replies allowed.