C++ primer 5th ed function template specialization - c++

Hello I have this code from C++ primer 5th ed:
Primary function template:
// first version; can compare any two types
template <typename T>
int compare(T const& x, T const& y)
{
std::cout << "compare(T const&, T const&)\n";
if(std::less<T>()(x, y))
return -1;
if(std::less<T>()(y, x))
return 1;
return 0;
}
A specialization for character arrays:
// second version to handle string literals
template <unsigned N, unsigned M>
int compare(char const(&ar1)[N], char const(&ar2)[M])
{
std::cout << "compare(char const(&)[N], char const(&)[M])\n";
return strcmp(ar1, ar2);
}
// special version of compare to handle pointers to character arrays
template <>
int compare(const char* const &p1, const char* const &p2)
{
std::cout << "compare(char const* const&, char const* const&)\n";
return strcmp(p1, p2);
}
int main()
{
const char *p1 = "hi", *p2 = "mom";
compare(p1, p2); // calls the third version (pointers to character strings)
compare("hi", "mom"); // calls the template with two nontype parameters
compare("high", "HIGH"); // error: call ambiguous
std::cout << "\nDone!\n";
}
I have some questions:
Is the version of compare with reference to arrays parameters a specialization or an overload? I think it is a specialization because its parameter list must match the one of the Primary function template compare. is it right?
The program works fine until I pass two arrays of characters or two literal character string with Same lengths. in which case the compiler cannot resolve the call like in my call compare("high", "HIGH");.:
Does this mean it fails because the version with arrays parameters is not viable? -because I guess that the Size of an array is a part of its type thus passing two arrays with different sizes yields two different types consequently this version is not viable?
The output of my compiler:
error: call of overloaded ‘compare(const char [5], const char [5])’ is ambiguous
candidate: ‘int compare(const T&, const T&) [with T = char [5]]’|
candidate: ‘int compare(const char (&)[N], const char (&)[M]) [with unsigned int N = 5; unsigned int M = 5]’
So how could I disambiguate this call? and please guide me about my guesses. Thanks

With respect to your first question: the function is an overload and not a specialization. There is no way to partially specialized a function template to start with. Beyond that, a specialization would mention an empty template parameter list. For example, the version for char const* is a specialization:
template <>
int compare(char const* x, char const& *);
With respect to your second question: it seems the compiler came to the conclusion that the first overload and the second overload are equally good. I'm not quite sure why that is as the version taking array references seems better. Adding another overload resolves that problem:
template <unsigned N>
int compare(char const(&ar1)[N], char const(&ar2)[N])
{
std::cout << "compare(char const(&)[N], char const(&)[N])\n";
return strcmp(ar1, ar2);
}
The version with two array sizes would be viable as well. It is just that the other overload is good, too. Instead of adding an overload, you could constrain the first version so it can't be used for arrays:
template <typename T>
std::enable_if_t<!std::is_array_v<T>, int> compare(T const& x, T const& y)
{
std::cout << "compare(T const&, T const&)\n";
if(std::less<T>()(x, y))
return -1;
if(std::less<T>()(y, x))
return 1;
return 0;
}

Related

function templates overloading versus fully-specializing

Hello I have this example:
#include <iostream>
template <typename T>
int compare(T const&, T const&);
template <typename T>
int compare(T*, T*);
template <>
int compare(char const * const&, char const* const&);
template <unsigned N, unsigned M>
int compare(char const (&)[N], char const(&)[M]);
template <unsigned N>
int compare(char const (&)[N], char const(&)[N]);
template <typename T>
int compare(T const& lhs, T const& rhs){
std::cout << "compare(T const&, T const&)\n";
if(std::less<T>()(lhs, rhs))
return -1;
if(std::less<T>()(rhs, lhs))
return 1;
return 0;
}
template <typename T>
int compare(T* p1, T* p2){
std::cout << "compare(T*, T*)\n";
if( std::less<T>()(*p1, *p2) )
return -1;
if( std::less<T>()(*p2, *p1) )
return 1;
return 0;
}
template <>
int compare(char const * const& p1, char const* const& p2){
std::cout << "compare(char const * const &, char const * const &)\n";
return strcmp(p1, p2);
}
template <unsigned N, unsigned M>
int compare(char const (&ra)[N], char const(&rb)[M]){
std::cout << "compare(char const(&)[N], char const(&)[M])\n";
return strcmp(ra, rb);
}
template <unsigned N>
int compare(char const (&ra)[N], char const(&rb)[N]){
std::cout << "compare(char const(&)[N], char const(&)[N])\n";
return strcmp(ra, rb);
}
int main(){
int a = 10, b = 57;
char const* const cp1 = "Hello";
char const* const cp2 = "World";
std::cout << compare(a, b) << '\n';
std::cout << compare(&a, &b) << '\n';
std::cout << compare(cp1, cp2) << '\n';
// std::cout << compare("Hi", "Hi") << '\n'; // error: ambiguous
// std::cout << compare("Hi", "World!") << '\n'; // error: ambiguous
cout << '\n';
}
Why the call to compare passing two arrays of of constant characters is ambiguous even the arrays of different lengths?
In fact the second overload that has a single non-type parameter N is just for compatibility with C++ 11 because on C++14 and above I can use std::enable_if. please
Why also passing compare(cp1, cp2); invokes compare(T*, T*) and not compare(char const* const&, char const * const&)?
One final question: Does my order of declaring the function templates here affects which function is preferred? Thank you.
Update:
I've manged to make it work by only changing the signature of the version taking two pointers to the parameter type: compare(T*, T*) to :
template <typename T>
int compare(T const * const&, T const* const&);
Now It works fine so can you explain why?
Question 1
Why the call to compare passing two arrays of of constant characters is ambiguous even the arrays of different lengths?
Answer 1
This is because when you write compare("Hi", "World!"); there are two version that can be used(equally good). First version is
template <typename T>
int compare(T*, T*);
And the second version is:
template <unsigned N, unsigned M>
int compare(char const (&ra)[N], char const(&rb)[M]);
Important Note
Note that the version:
template <>
int compare(char const * const& p1, char const* const& p2);
is a specialization and so it does not take part in overloading.To be more specific remember that:
Specializations instantiate a template; they do not overload it. As a result, specializations do not affect function matching.
Question 2
Why also passing compare(cp1, cp2); invokes compare(T*, T*) and not compare(char const* const&, char const * const&)?
Answer 2
This is because the version:
template <typename T>
int compare(T*, T*);
will be chosen for "all pointers" and more importantly it can take part in overloading while the version
template <>
int compare(char const * const& p1, char const* const& p2);
is a specialization and hence does not take part in overloading.
Question 3
Does my order of declaring the function templates here affects which function is preferred?
Answer 3
No
Why the call to compare passing two arrays of of constant characters is ambiguous even the arrays of different lengths?
Because both of
template <>
int compare(T*, T*);
template <unsigned N, unsigned M>
int compare(char const (&)[N], char const(&)[M]);
match in both cases, and in second case the third overload joins in as well (you might have specialised as template <unsigned N> compare<N, N>(...) to avoid ambiguity at least between those latter two.
Why also passing compare(cp1, cp2); invokes compare(T*, T*) and not compare(char const* const&, char const * const&)?
On selection of the right function template first overload resolution occurs and only when the right overload is selected template specialisations are considered. You now provide two template overloads of compare function:
template <typename T>
int compare(T const&, T const&);
template <typename T>
int compare(T*, T*);
Of these two, the pointer template is a better match, so this one is selected.
template <>
int compare(char const * const&, char const* const&);
is a specialisation of the overload that got discarded, though, and thus has been eliminated as candidate already before. 1)
By accepting pointers by value (char const* px) you'd have specialised the the other base template and thus would see the desired result.
Does my order of declaring the function templates here affects which function is preferred?
No. All overloads that are known at the time of calling the function are considered, no matter in which order they have been declared. If that wasn't the case it would be nearly impossible (at least only with significant research work) to predict which overload gets called if these are imported (possibly indirectly!) via different headers.
Edit (considering updated question):
template <typename T>
int compare(T const&, T const&);
template <typename T>
int compare(T const * const&, T const* const&);
template <>
int compare(char const * const&, char const* const&);
char const* still matches better T const* const& than T const&, so still second overload will be selected. If you now compare the involved signatures then your change provoked, though, the (actually unchanged) specialisation getting a specialisation of this second overload (char const* is closer to T const* const& than to T const&, just as was already during overload resolution).
As now the specialisation refers to the overload already having been selected, you now get the desired/expected result.
Side note: Leaving the original base template (T*) and changing the specialisation to
template <>
int compare(char const*, char const*);
would also have provoked the specialisation being a closer match to second overload – and this change would have been more conclusive as there's no reason for accepting a pointer by const reference – if not optimised away anyway, this is just yet another (needless) level of indirection (not so for non-const pointers, in which case you could apply a change to the pointer variable being passed to the function itself).
1)Adopted from Frank's answer which got deleted in the meanwhile.

Pass string literal to template function

I try to specialize a template function for a few types, one of them is const char*.
template<typename T = const char*>
void f(T&&);
template<>
void f<int>(int&& k) { std::cout << "Now in int fcn (" << k << ")!\n"; }
template<>
void f<const char*>(const char* && k) { std::cout << "Now in cc* fcn (" << k << ")!\n"; }
int main() {
f<int>(5);
f("HELLO");
return 0;
}
But when I execute f("HELLO"), I get the following error:
main.cpp:(.text+0x32): undefined reference to `void f<char const (&) [6]>(char const (&) [6])'
How do I make it interpret "HELLO" as a const char* and not an array? If I specialize for arrays I need one for each array size?
Also, the generic template function catches f("HELLO"):
template<typename T>
void f(T&& k) { /* Definition... */ }
Will it create one specialization for every array size I need or does it somehow cast the string literal to "const char*"?
A string literal is not a const char*. A string literal has the type const char[N] where N is the number of characters plus a null terminator. That means when you call the function T gets deduced to const char[6], which does not match any of the specializations so the main template is used. Since you have not defined the main template, you get a linker error about the definition missing.
You can add an overload of the function to handle char arrays and string literals by using
template<std::size_t N> void f(const char (&arr)[N]) { stuff; }
Yes, it will stamp out a function for each sized array, but that's just a little extra compilation time, you only need to write the body once.
You should also have a read of Why Not Specialize Function Templates?. In the article it details why function template specializations don't always work like you want them to and that they don't participate in overload resolution. Generally you should overload instead of specializing.

Template specialization unmatched error

Re-edited:
Here's what C++ Primer 5th says:
Version 1:
template <typename T> int compare(const T&, const T&);
Version 2:
template<size_t N, size_t M> int compare(const char (&)[N], const char (&)[M]);
A specialization of Version 1:
template <> int compare(const char* const &p1, const char* const &p2);
For example, we have defined two versions of our compare function template, one that takes references to array parameters and the other that takes const T&. The fact that we also have a specialization for character pointers has no impact on function matching. When we call compare on a string literal: compare("hi", "mom")
both function templates are viable and provide an equally good (i.e., exact) match to the call. However, the version with character array parameters is more specialized (§ 16.3, p. 695) and is chosen for this call.
The book says "both provide an equally good match", so then I thought putting Version 1 and its specialization should compile well. But it didn't.
So "provide an equally good match" doesn't mean it can compile? The book plays a trick on me?
Original code snippet link that I didin't understand why can't compile:
https://wandbox.org/permlink/oSCDWad03nELC9xs
Full context screenshot (I've boxed the most related part, sorry to post such a big pic here).
C-style strings are not pointers, they are arrays. When template type deduction happens, it deduces T as either const char[3] or const char[4]. Since those conflict the compiler is unable to deduce T and it stops there.
template<>
int compare(const char* const &p1, const char* const&p2) {
cout << "const char* const" << endl;
return 3;
}
won't be called because it relies on T being deduced and matching const char* and the compiler was not able to deduce T. A specialization is not a overload, it is a recipe for that specific T. If T can't be deduced then the specialization, even it it were to be a valid overload, won't be called.
If you were to overload the function instead of providing a specialization then it would compile with:
int compare(const char* const &p1, const char* const&p2) {
cout << "const char* const" << endl;
return 3;
}
You are passing to the template function two parameters of different types (the type of "hi" is const char [3] and the type of "mom" is const char [4]), so the compiler is not able to find a T that matches both types.
It's the same error that you would obtain calling std::min(0, 1U); std::min() (one of its overload) expects two arguments of the same type, as your compare() function does.
A possible solution to your problem is to accept parameters of different types:
template <typename T1, typename T2>
int compare(const T1&, const T2&);
This will work without editing the body of your function.
The compiler is unable to match it to one of the existing templates. If you read the section 16.5 carefully you will understand that it would call the second version of the template class.
The function call has 2 different types of parameters const char[3] and const char [4] compiler is unable to find a template specialization that takes 2 different data types as parameters.
The code below is one of the solutions.
#include <iostream>
#include <string>
using namespace std;
template <typename T> int compare(const T&, const T&) {
cout << "const T" << endl;
return 3;
}
template<size_t N, size_t M>
int compare(const char (&p)[N], const char (&q)[M]) {
cout<<p<<" "<<q<<endl;
return 3;
}
int main()
{
compare("hi", "mom");
}
The other solution is as below. It takes 2 different types and access the variables.
#include <iostream>
#include <string>
using namespace std;
template <typename T> int compare(const T&, const T&) {
cout << "const T" << endl;
return 3;
}
template <typename T1, typename T2>
int compare(const T1&p, const T2&q){
cout<<p<<" "<<q<<endl;
return 3;
}
int main()
{
compare("hi", "mom");
}

Rationale for overloading C-String and char pointers [duplicate]

I'm playing around with overloading operators in c++14, and I tried to match two types of arguments: any-old-const-char*, and a-string-literal.
That is, I'm trying to see if I can discriminate between:
const char * run_time;
and
"compile time"
I wrote the code below, and as shown, when I try span >> "literal" it invoked the const char* function.
When I #if 0-out the const char* version, the template version gets called just fine.
If I change the template version to take an rvalue-reference (&&) parameter for literal, it doesn't compile.
If I add a const char (&literal)[] non-template version, the const char* version is still preferred. Removing the const-char* version, the template version is preferred.
Can you explain this? In particular:
Why is const char* preferred over const char (&)[N]?
Why is const char (&)[N] preferred over const char (&)[] (non-template)?
Why is const char (&&)[N] unable to compile?
Is there a "right way" to capture literal strings?
Thanks.
#include <iostream>
using namespace std;
#include <gsl/gsl>
#include <type_name.h++>
template<unsigned N>
auto
operator>>(gsl::span<const char*,-1>& spn, const char (&literal)[N])
-> gsl::span<const char*, -1>&
{
cout << "Got array: " << literal << endl;
return spn;
}
auto
operator>>(gsl::span<const char*,-1>& spn, const char *literal)
-> gsl::span<const char*, -1>&
{
cout << "Got const-char*: " << literal << endl;
return spn;
}
#if 0
#endif
int
main(int argc, const char *argv[])
{
auto spn = gsl::span<const char*>(argv, argc);
cout << type_name<decltype(spn)>() << endl; // gsl::span<const char *, -1>
cout << type_name<decltype("literal")>() << endl; // char const (&)[8]
cout << type_name<decltype(("literal"))>() << endl; // char const (&)[8]
auto helpx = "literal";
cout << type_name<decltype(helpx)>() << endl; // const char *
spn >> "literal"; // Got const-char*: literal
return 0;
}
Edit:
In case it matters, I'm compiling with:
c++ --std=c++14 -Iinclude -c -o main.o main.c++
And c++ says:
$ c++ --version
Apple LLVM version 8.0.0 (clang-800.0.42.1)
Target: x86_64-apple-darwin16.5.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
Why is const char* preferred over const char (&)[N]?
The reason for this is rather technical. Even though the decay of a string literal from const char[N] to const char* is a conversion, it falls into the "lvalue transformation" category and is therefore considered by [over.ics.rank]/3 to be as good as no conversion at all. Since "no conversion" is required for either overload, the non-template overload wins.
Why is const char (&)[N] preferred over const char (&)[] (non-template)?
It is not possible to bind a reference to array of unknown bound to a value of type array of known bound. Instead, a reference to array of unknown bound can only be bound to values that are themselves arrays of unknown bound.
Why is const char (&&)[N] unable to compile?
A string literal is an lvalue so I'm not sure why you would expect this to work.
Is there a "right way" to capture literal strings?
You can use a helper function template that captures its argument using a forwarding reference so as to not destroy any type information (const char* versus const char[N]) then dispatch on the type using template specialization. You'll probably also want to use SFINAE to make sure it is disabled if anything other than a const char* or const char[N] is passed in. To wit,
template <bool b>
struct f_helper;
template <>
struct f_helper<true> {
void do_it(const char*) {
puts("pointer");
}
};
template <>
struct f_helper<false> {
template <std::size_t N>
void do_it(const char (&)[N]) {
printf("array of length %zd\n", N);
}
};
template <class T, class = typename std::enable_if<std::is_same<char*, std::decay_t<T>>::value ||
std::is_same<const char*, std::decay_t<T>>::value>::type>
void f(T&& s) {
f_helper<std::is_pointer<std::remove_reference_t<T>>::value>{}.do_it(s);
}
Coliru link: http://coliru.stacked-crooked.com/a/0e9681868d715e87
The overload taking a pointer is preferred because it is not a template according to
13.3.3 Best viable function [over.match.best]
Given these definitions, a viable function F1 is defined to be a better function than another viable function
F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then
...
(1.7)
F1 is not a function template specialization and F2 is a function template specialization
actually non-template const char (&)[] does not seem to compile at all because it is a reference to a non-bound array. It is possible to pass a pointer like this const char [], but not array.
this should fail at least for the same reason as (2)
you can provide another template taking a reference to pointer:
template< typename = void > void
foo(char const * & text)
{
::std::cout << "got ptr" << ::std::endl;
}
Note that providing another template taking a pointer won't work because both template specializations will be fine and we'll get ambiguous choice of overloaded functions.

Error message "deduced conflicting types for parameter 'const T'"

What I'm trying to do:
Write a specialized version of the template from the previous exercise to handle vector<const char*> and a program that
uses this specialization.
I wrote the program like this:
template<typename T>
int count(vector<T> tvec, const T &t);
template<>
int count(vector<const char *> tvec, const char *const &s)
{
int count = 0;
for (auto c : tvec)
if (c == s) {
++count;
}
return count;
}
template<typename T>
int count(vector<T> tvec, const T &t)
{
int count = 0;
for (auto c : tvec)
if (c == t) {
++count;
}
return count;
}
cout << count(svec, "GUO");
but I get the error that says
deduced conflicting types for parameter ‘const T’ (‘std::basic_string<char>’ and ‘char [4]’)
I want to know how to handle this. and further, in the template function, it seems that an array can be changed to the pointer, why my program cannot handle it?
Don't deduce on both parameters, it leads to conflicts. Write this:
template <typename T>
int count(const vector<T>& tvec, const typename vector<T>::value_type& t);
Also, consider overloading instead of specializing. Specializing a function template is pretty much never what you want.
Firstly, it seems svec is defined as vector<string>, maybe it should be vector<const char*>;
Secondly, explictly define a var as const char*;
Try this:
vector<const char*> svec;
const char* chars = "GUO";
std::cout<<my_count(svec,chars);
BTW: A variable of type char array(char[]) can be used as type char pointer(char*), but they are different as type, and they are different as a template paremeter.