1

还有另一个我无法解决的模板专业化问题:

终端日志.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);

而不是专业。显然我的代码中必须有一个错误,但是我找不到它。

4

7 回答 7

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).

于 2011-03-28T12:10:43.587 回答
3

在您的代码中,您定义了几个重载的函数模板(它们不是某些通用模板的特化,它们是单独的重载。但这没有错,没有理由它们必须是特化。)

其中一个模板的参数声明为const T v[]. 由于数组不能按值传递,因此编译器会对此进行解释,就像声明了参数一样const T *v

对于有问题的数组,最终是 type int[5],编译器必须在两个匹配的模板之间进行选择。根据标准中的 §13.3.3.1.1(表 10),最佳匹配取决于所需转换的数量和类型。

  • 模板const T&匹配. T = int[5]根据 §13.3.3.1.4/2 将 转换int[5]为 const参数需要与将 an 转换为constint(&)[5]相同的转换。这是一种资格转换(添加 a )。int[5]int[5]const
  • 模板const T*匹配. T = int将 a 转换int[5]为 aconst int*需要两次转换。首先是数组到指针的转换(int[5]to int*),然后是限定转换(int*to const int*)。

所有这些转换都符合“完全匹配”的条件,但由于第二个模板需要两次这样的转换,而第一个模板只需要一次,所以第一个模板是更好的匹配。

要获得“正确”匹配,您可以const从第二个模板的参数中删除 ,或者为仅调用 const 版本的非常量指针添加一个附加模板:

template <class T>
Terminallog& Terminallog::operator<<(T *v) {
   *this << static_cast<const T*>(v);
}

话虽如此,请注意,您无法sizeof在这样的函数中获取数组长度。Alexandre C. 在他的回答中建议的带有附加尺寸参数的模板可能是更好的选择。

于 2011-03-28T13:20:54.897 回答
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[]);

第一个是完全匹配,所以它“获胜”。

于 2011-03-28T12:16:25.380 回答
1

首先,您没有进行模板专业化:

template <class T>
Terminallog & operator<<(const T v[]);
Terminallog & operator<<(const char v[]);

是两个不同的功能。如果您尝试定义T类型为类型的输出,char那么您的编译器应该会抱怨模棱两可. 正如 ssteinberg 所说,要表明您正在专门研究模板,您需要使用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
于 2011-03-28T11:54:39.017 回答
1

首先:没有 extern 模板之类的东西(在 C++ 标准中有 export 关键字,但它被 MS 和 GNU 等主要编译器生产商忽略了,现在似乎被放弃了)。所以你必须把模板函数体放在头文件中。

第二:最好忘记部分模板专业化。它的支持不够好,例如,MS 仅对部分类模板特化(针对指针、引用、指向成员的指针和函数指针(看这里))提供非常有限的支持。所以最好不要使用它。但是您可以使用完全显式的模板特化。

第三:您的代码中实际上没有任何模板专业化

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++)。我认为,如果您有两个完全匹配或都不匹配的重载(并且需要隐式转换),您可能会遇到不合规的问题。

另请注意:

函数模板特化只允许在命名空间范围内。这意味着您不能声明成员函数模板特化。但当然,您可以像以前那样定义重载。

于 2011-03-28T12:36:40.713 回答
0

试着把 放在template<>前面Terminallog & operator<<(const char v[]);告诉编译器你正在专门化一个模板。

于 2011-03-28T11:33:08.360 回答
0

显式专用模板实例化:

inline template<>
Terminallog & operator<<(const char v[]);

像这样的东西。我的 C++ 生锈了。

于 2011-03-28T11:36:01.583 回答