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");
}
Related
Why pass a string literal to const char* const& in a specialized function template is illegal, while to const char* is legal?
Here's the thing. There are two excercises about template specialization in C++ Primer:
Exercise 16.63: Define a function template to count the number of
occurrences of a given value in a vector. Test your program by passing it a
vector of doubles, a vector of ints, and a vector of strings.
Exercise 16.64: Write a specialized version of the template from the
previous exercise to handle vector<const char*> and a program that
uses this specialization.
The code below is my answer, the compile error appears when I pass the string literal to this specialized count function.
// Count the number of occurrences of a given value in a vector
template <typename T>
std::size_t count(const std::vector<T>& vec, const T& value)
{
std::size_t i = 0;
for (const auto& v : vec) {
if (v == value)
++i;
}
return i;
}
// A specialized version where T = const char*
template <>
std::size_t count(const std::vector<const char*>& vec, const char* const& value)
{
std::size_t i = 0;
for (const auto& v : vec) {
if (!strcmp(v, value))
++i;
}
return i;
}
int main()
{
std::vector<const char*> sVec{ "cpp", "primer", "cpp", "fifth", "edition", "Cpp", "cpp" };
// Error message: no instance of function template "count" matches the argument list,
// argument types are: (std::vector<const char *, std::allocator<const char *>>, const char [4])
std::cout << count(sVec, "cpp") << std::endl;
return 0;
}
Besides, it's perfectly ok to pass a string literal to const char* const& in a nontemplate function, which makes me confused.
void test(const char* const& str)
{
std::cout << str << std::endl;
}
int main()
{
test("cpp"); // Prints "cpp" as expected
return 0;
}
You don't "pass to a function template specialization". The call is deduced against the original template definition (regardless of any specialization), deducing a type if successful. Then if a specialization exists for the deduced type then that will be called.
The error message is about type deduction failing . You should see the exact same error even if you delete the specialization.
The type deduction fails because T occurs in two different parameters but the result of deduction differs for each parameter:
Deducing vector<T>& vec against vector<const char *> produces T=const char *
Deducing const T& value against "cpp" produces T=const char[4] .
Deduction only succeeds if all instances of T being deduced produce the same type. There is no extra step of different types being reconciled by considering available conversions.
One way to solve the problem would be to use overloading instead of specialization (i.e. delete the text template <>). Then there is an overload set consisting of the non-template function, and the result of deduction (if any). If deduction fails , as it does, it is not an error because the overload set still contains something.
An array and a pointer are different types.
It is because of default conversions, that you can pass an array as a pointer parameter.
A template is just that, a "template" for a function.
There is no concrete function to match the parameters that have been specified. So the compiler tries to generate a function from the parameters and can't make a match, so it fails.
But when you specify the template parameter, the compiler knows exactly which function you want, generates it, then tries to call the function using standard rules for calling functions, which allow it to make the conversion.
When you have a template function definition, the requirements for function arguments are stricter.
In this case it will try to match the array and generate the function from the template and Fail. So no function will be available to call.
You can specialize:
// A specialized version where T = const char[ArraySize]
template<std::size_t ArraySize>
std::size_t count(const std::vector<const char*>& vec, const char (&value)[ArraySize])
{
return count<const char*>(vec, value);
}
-- Edit
Or specifically defining a template parameter function, which defines a concrete function to call.
Oh, I changed the code to std::cout << count<const char*>(sVec, "cpp") << std::endl; Then everything is fine. It seems like compiler treats the string literal "cpp" as a const char [4] rather than const char* const&, so I have to explicitly specify the template parameter. Wonder why that happened.
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.
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;
}
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.
This is an example from C++ Primer, 4th edition, Chapter 16 and it's about template specialization.
template <class T>
int compare(const T& v1, const T& v2) {
if(v1 < v2) return -1;
if(v2 < v1) return 1;
return 0;
}
template <>
int compare<const char*>(const char* const &v1, const char* const &v2){
return strcmp(v1, v2);
}
int main(int argc, const char *argv[])
{
cout << compare("abc", "defg") << endl;
return 0;
}
I expect compare("abc", "defg") will call the specialized version of the template.
But the fact is, g++ 4.6.3 won't compile this code and give the follow error:
error: no matching function for call to 'compare(const char [4], const
char [5])'
note: candidate is: template int compare(const T&,
const T&)
Now given the following facts:
I. string literals, or C-style string in C++ is actually a const
char array.
II. If passed as plain, non-reference types, an array will be
converted to a pointer to its first element quietly.
Here I just pass string literals "abc" and "defg" as reference to const char*, and I expect they will be converted to const char* first and then passed by reference.
But it seems that g++ disagree with me and refuse to compile the code.
But if I replace template specialization with function overloading, that is, replace:
template <>
int compare<const char*>(const char* const &v1, const char* const &v2){
return strcmp(v1 ,v2);
}
With:
int compare(const char* const& v1, const char* const& v2){
return strcmp(v1, v2);
}
Then g++ will be happy to compile it.
So where on earth does the problem lie? Why I cannot pass string literals by parameter type const char* const& in the template specialization version?
The answer below is based on explanation from C++ Templates: The complete guide pp57: Using String literals as Arguments for Function templates.
template <class T>
int compare(const T& v1, const T& v2)
{
if (v1 < v2) return -1;
if (v2 < v1) return 1;
return 0;
}
This requires that both parameters v1 and v2 have the same type.
template <>
int compare<const char*>(const char* const &v1, const char* const &v2){
return strcmp(v1, v2);
}
This requires that you have parameters with const char * type.
However, "abc" has type char const[4] whereas "defg" has type char const[5]. They are different types. Since both the specialized and templated version required reference parameters, there is no array-to-pointer decay during argument deduction. Therefore, you cannot pass different length string literals to both of them to find a match. If you provide a regular function, which does not require any argument deduction, the compiler will find a match.
If you declare non-reference parameters, you can substitute them with string literals of different length. The reason for this behavior is that during argument deduction array-to-pointer conversion (often called decay) occurs only if the paramter does not have a reference type.
Template specializations do not participate in overload resolution process. Only the primary template is considered by overload resolution.
Template specializations come into play only later and only if their primary template "wins" overload resolution. I.e. template specializations are used in the process of specialization (as the name suggests), they are completely invisible during overload resolution.
For this reason, in your first example, you have only one candidate considered by overload resolution
template <class T> int compare(const T& v1, const T& v2);
In order to succeed, this candidate should pass through template argument deduction for your set of arguments. (Template argument deduction process does not care about any additional specializations either.) Template argument deduction fails in this case, since for argument of array type template parameter T is deduced as an array. And you get incompatible deductions for two arguments. The compiler gave you the error message that describes the problem. In other words, in your first example the specialized version of the template never has a chance to come into play.
In your second example, where you replaced specialization with overloading, you provided a second candidate for overload resolution. Now the compiler sees both
template <class T> int compare(const T& v1, const T& v2);
int compare(const char* const& v1, const char* const& v2);
The template candidate fails just like it did before, while the overloaded candidate succeeds.
To better illustrate the how template specializations work in this case, we can take your original code and change the primary template in order to help it to pass through overload resolution by decoupling the parameters from each other. If in your first example you change the template declaration to
template <class T1, class T2>
int compare(const T1& v1, const T2& v2) {
...
leaving everything else unchanged, the code will compile and it will use your specialization. But even in that case the primary template with deduced parameters will be seen as a better match to your arguments (immediate reference binding with no conversions).