Reputation: 1584
And another template specialization problem, which I can't resolve:
terminallog.hh
//stripped code
class Terminallog {
public:
Terminallog();
Terminallog(int);
virtual ~Terminallog();
template <class T>
Terminallog & operator<<(const T &v);
template <class T>
Terminallog & operator<<(const std::vector<T> &v);
template <class T>
Terminallog & operator<<(const T v[]);
Terminallog & operator<<(const char v[]);
//stripped code
};
terminallog.hh continued (edited thanks to comment)
//stripped code
template <class T>
Terminallog &Terminallog::operator<<(const T &v) {
std::cout << std::endl;
this->indent();
std::cout << v;
return *this;
}
template <class T>
Terminallog &Terminallog::operator<<(const std::vector<T> &v) {
for (unsigned int i = 0; i < v.size(); i++) {
std::cout << std::endl;
this->indent();
std::cout << "Element " << i << ": " << v.at(i);
}
return *this;
}
template <class T>
Terminallog &Terminallog::operator<<(const T v[]) {
unsigned int elements = sizeof (v) / sizeof (v[0]);
for (unsigned int i = 0; i < elements; i++) {
std::cout << std::endl;
this->indent();
std::cout << "Element " << i << ": " << v[i];
}
return *this;
}
inline
Terminallog &Terminallog::operator<<(const char v[]) {
std::cout << std::endl;
this->indent();
std::cout << v;
return *this;
}
//stripped code
This compiles just fine, no errors. However when I try to do something like:
Terminallog clog(3);
int test[] = {5,6,7,8};
clog << test;
it always prints me the pointer-address the of the array. In other words the specialized template
Terminallog & operator<<(const T v[]);
is never called. I also verified this with an additional cout. No matter what I try the program is always calling
Terminallog & operator<<(const T &v);
and not the specialization. Obviously there has to be an error in my code, however I can't find it.
Upvotes: 1
Views: 1577
Reputation: 5299
First of all you are not doing template specialization:
template <class T>
Terminallog & operator<<(const T v[]);
Terminallog & operator<<(const char v[]);
are two different functions. If you tried to define an output where . To indicate that you are specializing a template, as ssteinberg notes, you need to use the T
is of type char
then your compiler should complain of an ambiguitytemplate<>
notation.
In this case, however, this probably won't help you as I don't believe you can specialize member functions (maybe you can if they are static?). So your compiler will complain if you try to follow ssteinberg's advice. You need to template the whole class, and then specialize individual functions.
The following link might provide some help, http://womble.decadent.org.uk/c++/template-faq.html#mem-fun-specialisation
EDIT:
The following might be illustrative:
#include <vector>
#include <iostream>
template<class T>
class Terminallog {
public:
Terminallog(){};
Terminallog(int){};
virtual ~Terminallog(){};
//general vector output: will be specialized for vectors of chars
Terminallog &
operator<<(const std::vector<T> &v);
//general reference output: will be specialized for chars
Terminallog & operator<<(const T &v);
//general pointer output: will be specialised for char pointers
Terminallog & operator<<(const T* v);
//stripped code
};
//general code for reference type
template <class T>
Terminallog<T>&
Terminallog<T>::operator<<(const T &v) {
std::cout<<"This general reference"<<std::endl;
return *this;
}
//specialisation for chars reference
template <> //as noted by ssteinberg
Terminallog<char>&
Terminallog<char>::operator<<(const char &v) {
std::cout<<"This is for chars"<<std::endl;
return *this;
}
//general code for pointer type
template <class T>
Terminallog<T>&
Terminallog<T>::operator<<(const T* v) {
std::cout<<"This general pointers"<<std::endl;
return *this;
}
//specialisation for chars pointer
//as noted by alexandre your array will decay to this....
template <>
Terminallog<char>&
Terminallog<char>::operator<<(const char* v) {
std::cout<<"This is for chars pointers"<<std::endl;
return *this;
}
//Non specialised vector
template <class T>
Terminallog<T>&
Terminallog<T>::operator<<(const std::vector<T> &v) {
std::cout<<"This general vector"<<std::endl;
return *this;
}
//specialisation for vector of chars
template <>
Terminallog<char>&
Terminallog<char>::operator<<(const std::vector<char> &v) {
std::cout<<"This is a vector of chars"<<std::endl;
return *this;
}
int
main (int ac, char **av)
{
Terminallog<int> ilog(3);
int testint[] = {5,6,7,8};
std::vector<int> testvi;
testvi.push_back(1);
testvi.push_back(3);
testvi.push_back(5);
Terminallog<char> clog(3);
char testchar[] = {5,6,7,8};
std::vector<char> testvc;
testvc.push_back(1);
testvc.push_back(3);
testvc.push_back(5);
ilog << testint;
ilog << testvi;
clog << testchar;
clog << testvc;
}
Output is
This general pointers
This general vector
This is for chars pointers
This is a vector of chars
Upvotes: 1
Reputation: 4429
First: there is no such thing as extern templates (there was export keyword in C++ standard but it was ignored by major compiler producers like MS and GNU and now seems abandoned). So you have to put template function bodies in header file.
Second: better forget partial template specialization. It is not supported well enough, e.g. MS provides only very limited support for partial class template specialization (for pointers, references, pointer to member and function pointers (Look here)). So better just don't use it. But you can use fully explicit template specialization.
Third: you don't really have any template specializations in your code
template <class T>
Terminallog & operator<<(const T &v);
template <class T>
Terminallog & operator<<(const std::vector<T> &v);
template <class T>
Terminallog & operator<<(const T v[]);
are three distinct function templates and
Terminallog & operator<<(const char v[]);
is just function.
The right syntax for function template specializations is this
template <class T>
Terminallog& out(const T& v)
{
// default implementation
}
template <class T>
Terminallog& out< std::vector<T> >(const std::vector<T>& v)
{
// partially specialized implementation
}
template <>
Terminallog& out<double>(const double& v)
{
// fully specialized implementation
}
But it is not really the point. The overloading resolution must still lead to the most specialized function or function template (if no such function exist) according to the rules defined in the standard. But I'm not sure that fully compliant C++ implementation exist (except Comeau C++ developed by standard authors used by nobody). I think that if you have two overloads that match exactly or none of them (and implicit conversion is required) you may have problems with non-compliance.
ALSO NOTE:
Function template specializations are allowed in namespace scope only. It means that you may not declare member function template specializations. But of course you may define overloads like you did.
Upvotes: 1
Reputation: 229593
In your code, you define several overloaded function templates (They are not specializations of some generic template, they are separate overloads. But there is nothing wrong with that, there is no reason they would have to be specializations.)
One of these templates has a parameter declaration of const T v[]
. Since arrays cannot be passed by value, this is interpreted by the compiler just the same as if the parameter was declared const T *v
.
For the array in questions, which ends up to be of type int[5]
, the compiler has to chose between two matching templates. The best match is determined by the number and type of conversions needed, according to §13.3.3.1.1 (Table 10) in the standard.
const T&
template matches for T = int[5]
. According to §13.3.3.1.4/2 converting the int[5]
to a const int(&)[5]
parameter requires the same conversions as converting an int[5]
to a const int[5]
. This is one qualification conversion (adding a const
).const T*
template matches for T = int
. Converting a int[5]
to a const int*
requires two conversions. First and array-to-pointer conversion (int[5]
to int*
), then a qualification conversion (int*
to const int*
).All these conversions qualify as "exact matches", but since the second template would require two such conversions while the first template requires only one, the first template is a better match.
To get the "correct" match, you could remove the const
from the parameter of the second template, or add an additional template for non-const pointers that just calls the const version:
template <class T>
Terminallog& Terminallog::operator<<(T *v) {
*this << static_cast<const T*>(v);
}
All that being said, note that you cannot get the array length with sizeof
in a function like this. A template with an additional size parameter like suggested by Alexandre C. in his answer might be a better choice for that.
Upvotes: 3
Reputation: 56956
My bet is that conversion rules are applied here. Since there is no exact match for int [5]
(which is the actual type of your array), your array will decay to int*
and the overload with const T&
will be chosen, since it will be a better match than const T v[]
(which is treated as const T* v
).
See @sth 's answer for a detailed explanation on the overload resolution mechanism in this case.
What if you try:
template <class T, size_t n>
Terminallog & operator<<(const T (&v)[n]);
instead ?
By the way the sizeof
stuff in the definition of the overload with T[]
is plain wrong. You cannot get the size this way. Again the array will decay to a pointer and elements
will always be sizeof(T*) / sizeof(T)
.
Upvotes: 3
Reputation: 8053
First of all, you don't have specializations here, but overloaded functions.
Then, I assume the problem is as follows:
int test[] = {5,6,7,8}; // <-- this guy is "decayed" to int* in next call
clog << test;
So now during overloading resolution compiler chooses between
template <class T>
Terminallog & operator<<(const T &v);
template <class T>
Terminallog & operator<<(const T v[]);
First one is exact match, so it "wins".
Upvotes: 2
Reputation: 6139
Explicit specialized template instantiation:
inline template<>
Terminallog & operator<<(const char v[]);
Something like this. My C++ is rusty.
Upvotes: 0
Reputation: 38163
Try to put template<>
in front of Terminallog & operator<<(const char v[]);
to tell the compiler that you're specializing a template.
Upvotes: 0