How does C++ function template specialization work? - c++

I am reading C++ Primer (5th Edition), 16.5, Defining a Function Template Specialization, and confused about the example given by author, let's see the following template function:
template <typename T> int compare(const T&, const T&);
and its specialization version:
template <>
int compare(const char* const &p1, const char* const &p2)
{
return strcmp(p1, p2);
}
Type of T will be const char *, but I don't think the function could be the specialization version of the template function, because I think const char* const &p1 can only be the specialization of T const & but const T &, I know I'm wrong, but I want to know why I am wrong.
Edit:
One thing to emphasize, if I invoke compare("hi", "mom"), it won't compile, that is to say, template <typename T> int compare(const T&, const T&) can not be initialized to int compare(const char* const &p1, const char* const &p2), I know T will be initialized with char[3] or char[4], but now that this won't compile, why won't compiler ignore this kind of initialization but choose one that will compile?

In C++, const T and T const mean exactly the same thing. Therefore const T & and T const & mean exactly the same thing.
It couldn't really be any other way, because references can never be changed to refer to something else (they are not "reseatable"). If you read T const & as "a reference which cannot be changed to refer to a different T", that's incorrect. It is "a reference to a T, which cannot be used to modify that T (and as with all references, cannot be changed to refer to a different T)."

const can be either before or after a type, except for a pointer type, where it must be on the right side.
Now the tricky part here is, that T in your example is set to const char*, which is a pointer type (pointer to a const char). The template says that T has to be const, and since it's a pointer type the const in the specialization has to be put after the type, hence you get const char* const.
It becomes a bit more clear when reading it aloud from right to left:
"a const pointer to a char that is const"
//Edit:
Why can't you call compare("hi", "mom");? Because those char arrays are treated as different types by your compiler (char[3] and char[4]), but the template specifies both parameters to be the same type.
This will match the template, however it will not match your specialization (as T is now char[2]):
compare("a", "b");
This works and uses your specialized method:
const char * hi = "hi";
const char * mom = "mom";
compare(hi, mom);
//Edit2:
"I know T will be initialized with char[3] or char[4], [...] why won't compiler ignore this kind of initialization but choose one that will compile?"
Because C++ is a strongly typed language. The compiler doesn't do guess work for you, it takes the types at face value. If they don't match, they don't match. It's your job as developer to do it right.

Related

Why use a reference to get a *char? Isn't better to not use references when we are using pointers?

(I will have a set of questions at the end)When i have to create a function that takes a pointer const char* as example, i never use a reference because i thought it makes no sense to use a reference to a pointer. But in a book (C++ Primer Fifht Edition) the author tell us that we have to create out version of a funcition called compare to compare our c-style string so we avoid using the general template version
template <>
int compare(const char* const &p1, const char* const &p2)
{ return strcmp(p1, p2); }
Yes he's actually declaring that the pointers are const. And, the author actually write another function that only takes string literals
template<size_t N, size_t M>
int compare(const char (&)[N], const char (&)[M]);
So my questions are:
Why is he using a reference to get a pointer? Copy a pointer or take a reference are equally expensive, isn't it?
Can't the first version take string literals?
Why is he using a template for a function that does not require template paremeters? He is trying to overload another template funcion put thats not enought reason, is it?
Why is he using a reference to get a pointer? Copy a pointer or take a reference are equally expensive, isn't it?
I am guessing that there is a function template:
template <typename T>
int compare(T const &t1, T const &t2) { ... }
and it is used elsewhere in the code base.
To make a specialization of the template for char const*, you'll have to use:
template <>
int compare(char const* const &p1, char const* const &p2) { ... }
// <-- T -->
Can't the first version take string literals?
Yes, it can.
Why is he using a template for a function that does not require template paremeters? He is trying to overload another template funcion put thats not enought reason, is it?
That's the syntax for specializing a function template. It can be a function overload instead of a function template only if the function is called without explicitly specifying the template parameter.
If you define
int compare(char const* const &p1, char const* const &p2) { ... }
it won't be called when the caller uses compare<char const*>(s1, s2).
It will be called when the callers uses compare(s1, s2).

Why is the const qualifier in this type alias dropped? [duplicate]

This question already has answers here:
constant references with typedef and templates in c++
(5 answers)
Closed 4 years ago.
TL;DR
Given the following type:
struct A
{
std::vector<std::string> vec;
using reference = std::iterator_traits<decltype(vec)::iterator>::reference;
using const_reference = const reference;
};
Why is reference == const_reference? Why is the const qualifier dropped in the second type alias?
See the example on godbold which shouldn't compile.
Details
I have a templated class that takes a bunch of iterators (-types) as template arguments. From these iterators I need to deduce the reference and const reference type because I have some member functions like:
struct A
{
std::vector<std::string> vec;
using reference = std::iterator_traits<decltype(vec)::iterator>::reference;
using const_reference = const reference;
const_reference foo() const
{
return vec[0];
}
};
By dropping the const qualifier, I'm effectively returning a reference in foo which is illegal because it's a const member function and so the compiler throws.
It is dropped. What we call a "const reference" is really a reference to const - const int&.
Having an int& and adding const would make that int& const. But such a const is dropped.
Your problem is similar to the difference between const int* and int* const. Except that for references, int& and int& const is the same type - the const is ignored.
Your problem is west const. West const is bad const east const is best const.
West const is putting the const on the left of the token you want to be const. East const is putting it on the right.
If I told you never to put const on the left of your types, and that const always applies to the thing on its left, look at this:
using const_reference = reference const;
you can probably work out why the const didn't work. After expanding reference naively you get string&const -- here you attempt to apply const to & not string, and a foo &const is not the same as a foo const& -- a foo&const is just a foo& as const cannot apply to the reference itself, but only to the type referred to.
Sure, you say, but that is why I want west const!
West const does the same thing here. It applies to the & not then string and then is discarded. It just does so in a way that is more confusing and harder to grasp intuitively.
To understand const, convert your const to east const. West const is just an exception to the standard east const rule, where if there is no token in the type to the left it applies to the token on the right. The token on the right here is the entire type string& bundled into a type alias (type aliases are not macros). If instead you typed const string& the token on the right would be string and you'd get string const& in sane, east-const style.
East const is best const.

Template argument deduction for 'char*'

I have below function (Just for reproducing the issue):
template <typename KeyT>
void func(const KeyT cptr) {
std::cout << typeid(KeyT).name() << std::endl;
}
I would like to call this with a string literal, like below:
func<char*>("literal");
But, I end up getting below warning:
warning: ISO C++11 does not allow conversion from string literal to 'char *' [-Wc++11-compat-deprecated-writable-strings]
I have a specific need to use char* as my Key type and I was expecting TAD to consider the param type as const char* in whole as I am not taking it by reference.
The warning comes with both clang and g++ compilers.
How is the param type being deduced here ?
Thanks in advance.
I was expecting TAD to consider the param type as const char* ...
How is the param type being deduced here ?
template <typename KeyT>
void func(const KeyT cptr)
Note that const is the qualifier on KeyT itself, it means if KeyT is a pointer, then cptr will be a const pointer, not a pointer to const.
"literal" is sring literal with type const char[8], which might decay to const char*. Then KeyT might be deduced as const char*, then the type of cptr will be const char* const.
You're specifying the template argument type as char*, then makes cptr a char* const. But from C++11, converting a string literal to char* implicitly is not allowed since literals are const.
Why do you need the template parameter type to be char*? To modify it inside the function? Note that modifying a string literal will lead to UB. You could pass a char array to it, like:
char my_chararray[] = "literal";
func<char*>(my_chararray); // or just func(my_chararray);
There is no deduction here: you have explicitly stated that the template parameter is char*.
What you're missing is that parameter substitution is not a text search-and-replace: it actually follows the logical rules for the type system. const KeyT cptr declares a const instance of the type KeyT — when KeyT is char*, the parameter declaration becomes char *const cptr.

C++ const char* overloading confusion

I don't understand why this program produces the output below.
void blah(const char* ) {printf("const char*\n");}
void blah(const std::string&) {printf("const string ref\n");}
template<class t>
void blah(t) {printf ("unknown\n");}
int main(int, char*)
{
blah("hi");
char a[4];
blah(a);
std::string s;
blah(s);
getch();
}
Outputs:
const char*
unknown
const string
In VS2008. It is willing to convert the std::string to a const reference, but why won't it convert the char* to a const char* and use the overload?
The type of "hi" is const char[3], whereas the type of a is char[4].
So, the first call requires only array-to-pointer conversion (aka "decay"). The third call requires only binding an object to a reference-to-const (I don't think "converting" is the correct terminology for reference-binding, although I may be mistaken). The second call would require array decay and a pointer conversion in order to call the const char* overload.
I claim without actually checking the overload resolution text in the standard that this extra step is what makes the template a better match than the const char* overload.
Btw, if you change "unknown\n" to "%s\n", typeid(t).name() then you can see what the type t was deduced as. For your code, it is deduced as char* (because arrays can't be passed by value), but see what happens if you change the template to take a t& parameter instead of t. Then t can be deduced as char[4].

Template neglects const (why?)

Does somebody know, why this compiles??
template< typename TBufferTypeFront, typename TBufferTypeBack = TBufferTypeFront>
class FrontBackBuffer{
public:
FrontBackBuffer(
const TBufferTypeFront front,
const TBufferTypeBack back): ////const reference assigned to reference???
m_Front(front),
m_Back(back)
{
};
~FrontBackBuffer()
{};
TBufferTypeFront m_Front; ///< The front buffer
TBufferTypeBack m_Back; ///< The back buffer
};
int main(){
int b;
int a;
FrontBackBuffer<int&,int&> buffer(a,b); //
buffer.m_Back = 33;
buffer.m_Front = 55;
}
I compile with GCC 4.4. Why does it even let me compile this? Shouldn't there be an error that I cannot assign a const reference to a non-const reference?
The thing is that if type T is int&, then the type const T is not const int&, but int & const. The illegal top-level const on a reference is ignored in template substitutions and typedef results.
If, on the other hand T is const int, then T& is const int&
When TypeBufferFront is int&, const TBufferTypeFront is equivalent to int& const, where the const is ignored during template substitution, since all references are constant, even if what they refer to is not.
So, when instantiated with int&, your constructor is effectively FrontBackBuffer(int&, int&), which works as given.
This is an example of why many people will use T const instead of const T, to make it clearer how the substitution occurs, as well as allow them to read the cv-qualifiers from right to left.
For the code to do what you want it to do, it would have to read:
FrontBackBuffer(
typename std::remove_reference<TBufferTypeFront>::type const& m_front,
typename std::remove_reference<TBufferTypeBack>::type const& m_back): ////const reference assigned to reference???
m_Front(m_front),
m_Back(m_back)
{
};
which has the added "feature" that it turns other types into const references when used to construct FrontBackBuffer.
Now this isn't perfect. This prevents temporary arguments to FrontBackBuffer from being moved, and passes even small cheap to copy types (like char) by reference instead of by value. There are standard C++0x techniques to do this that are a bit awkward to write if you care.
FrontBackBuffer::m_Front is of type TBufferTypeFront which translates to int& in your template instantiation. There is nothing wrong with assigning to an int&.