另一个模板特化问题,我无法解决:
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续(编辑感谢评论)
//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
编译得很好,没有错误。但是,当我尝试做类似的事情时:
Terminallog clog(3);
int test[] = {5,6,7,8};
clog << test;
它总是打印出数组的指针地址。换句话说,专门的模板
Terminallog & operator<<(const T v[]);
永远不会打电话给。我还通过额外的cout验证了这一点。无论我尝试什么,程序总是在调用
Terminallog & operator<<(const T &v);
而不是专业化。显然我的代码中必须有一个错误,但我找不到它。
答案 0 :(得分:3)
我敢打赌,这里会应用转换规则。由于int [5]
(这是您的数组的实际类型)没有完全匹配,因此您的数组将衰减到int*
,并且将选择带const T&
的重载,因为它将是比const T v[]
更好的匹配(被视为const T* v
)。
有关此情况下的重载解析机制的详细说明,请参阅@sth的答案。
如果你尝试怎么办?
template <class T, size_t n>
Terminallog & operator<<(const T (&v)[n]);
相反?
顺便说一下,sizeof
重载定义中的T[]
内容是完全错误的。你无法通过这种方式获得尺寸。同样,数组将衰减为指针,elements
将始终为sizeof(T*) / sizeof(T)
。
答案 1 :(得分:3)
在你的代码中,你定义了几个重载的函数模板(它们不是某些通用模板的特化,它们是单独的重载。但是没有任何问题,没有理由它们必须是特化。)
其中一个模板的参数声明为const T v[]
。由于数组不能通过值传递,因此编译器会对此进行解释,就像声明参数const T *v
一样。
对于问题中的数组,最终类型为int[5]
,编译器必须在两个匹配的模板之间进行选择。根据标准中的§13.3.3.1.1(表10),最佳匹配取决于所需转换的数量和类型。
const T&
模板与T = int[5]
匹配。根据§13.3.3.1.4/ 2将int[5]
转换为const int(&)[5]
参数需要与将int[5]
转换为const int[5]
相同的转换。这是一次资格转换(添加const
)。const T*
模板与T = int
匹配。将int[5]
转换为const int*
需要进行两次转换。首先是数组到指针的转换(int[5]
到int*
),然后是资格转换(int*
到const int*
)。所有这些转化都符合“完全匹配”,但由于第二个模板需要两次此类转换,而第一个模板只需要一次,因此第一个模板更匹配。
要获得“正确”匹配,可以从第二个模板的参数中删除const
,或者为仅调用const版本的非常量指针添加其他模板:
template <class T>
Terminallog& Terminallog::operator<<(T *v) {
*this << static_cast<const T*>(v);
}
所有这一切,请注意,在这样的函数中,您无法使用sizeof
获取数组长度。具有额外大小参数的模板,如Alexandre C.在他的回答中建议的那样,可能是更好的选择。
答案 2 :(得分:2)
首先,你没有专业化,但是功能过载。
然后,我认为问题如下:
int test[] = {5,6,7,8}; // <-- this guy is "decayed" to int* in next call
clog << test;
所以现在在重载分辨率编译器之间选择
template <class T>
Terminallog & operator<<(const T &v);
template <class T>
Terminallog & operator<<(const T v[]);
第一个是完全匹配,所以它&#34;胜出#34;。
答案 3 :(得分:1)
首先,您没有进行模板专业化:
template <class T>
Terminallog & operator<<(const T v[]);
Terminallog & operator<<(const char v[]);
是两个不同的功能。 如果您尝试定义。为了表明您正在专门化模板,正如ssteinberg所说,您需要使用T
类型为char
的输出,那么您的编译器应该抱怨歧义template<>
表示法。
但是,在这种情况下,这可能对您没有帮助,因为我不相信您可以专门化成员函数(如果它们是静态的,也许你可以吗?)。因此,如果您尝试遵循ssteinberg的建议,您的编译器会抱怨。您需要模拟整个类,然后专门化各个函数。
以下链接可能会提供一些帮助,http://womble.decadent.org.uk/c++/template-faq.html#mem-fun-specialisation
修改强>
以下可能是说明性的:
#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;
}
输出
This general pointers
This general vector
This is for chars pointers
This is a vector of chars
答案 4 :(得分:1)
首先:没有外部模板这样的东西(在C ++标准中有导出关键字,但它被MS和GNU等主要编译器生产者忽略,现在似乎已经放弃了)。所以你必须将模板函数体放在头文件中。
第二:最好忘记部分模板专业化。它的支持不够好,例如MS仅对部分类模板特化提供非常有限的支持(对于指针,引用,指向成员和函数指针(Look here)的指针)。所以最好不要使用它。但您可以使用完全显式的模板专业化。
第三:您的代码中没有任何模板专业化
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[]);
只是功能。
函数模板特化的正确语法是这个
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
}
但这不是重点。根据标准中定义的规则,重载决策仍必须导致最专业的功能或功能模板(如果不存在此类功能)。但我不确定是否存在完全兼容的C ++实现(除了由任何人使用的标准作者开发的Comeau C ++)。我认为如果你有两个完全匹配的重载或者没有一个重载(并且需要隐式转换),你可能会遇到不合规的问题。
还注意:
仅在命名空间作用域中允许使用函数模板特化。这意味着您可能不会声明成员函数模板特化。但是你当然可以像你一样定义重载。
答案 5 :(得分:0)
尝试将template<>
放在Terminallog & operator<<(const char v[]);
前面,告诉编译器您正在专门化模板。
答案 6 :(得分:0)
明确的专门模板实例化:
inline template<>
Terminallog & operator<<(const char v[]);
像这样的东西。我的C ++生锈了。