专业 C++从未调用过模板方法

发布于 2024-10-27 17:10:38 字数 2112 浏览 1 评论 0原文

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

terminallog.hhterminallog.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 
};

继续(感谢评论进行编辑)

//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);

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

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

巡山小妖精 2024-11-03 17:10:38

我敢打赌,这里应用了转换规则。由于 int [5] (这是数组的实际类型)没有完全匹配,因此您的数组将衰减为 int* 以及 的重载const T& 将被选择,因为它将比 const T v[] (被视为 const T* v)更好的匹配。

有关这种情况下重载解析机制的详细说明,请参阅 @sth 的答案。

如果你尝试:

template <class T, size_t n>
Terminallog & operator<<(const T (&v)[n]);

相反,会怎样?

顺便说一句,T[] 重载定义中的 sizeof 内容是完全错误的。您无法通过这种方式获得尺寸。同样,数组将衰减为指针,并且元素将始终为sizeof(T*) / sizeof(T)

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

念三年u 2024-11-03 17:10:38

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

其中一个模板具有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] 参数需要与转换 < code>int[5] 到 const int[5]。这是一次限定转换(添加 const)。
  • const T* 模板与 T = int 匹配。将 int[5] 转换为 const int* 需要两次转换。首先是数组到指针的转换(int[5]int*),然后是限定转换(int*常量 int*)。

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

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

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

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

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.

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

猥琐帝 2024-11-03 17:10:38

首先,这里没有专业化,而是重载函数。

然后,我假设问题如下:

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[]);

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

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

时光是把杀猪刀 2024-11-03 17:10:38

首先:不存在 extern 模板之类的东西(C++ 标准中有导出关键字,但它被 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++ 之外)。我认为,如果您有两个完全匹配的重载或没有一个重载(并且需要隐式转换),您可能会遇到不合规问题。

另请注意:

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

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.

|煩躁 2024-11-03 17:10:38

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

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-specialization

编辑:

以下内容可能是说明性的:

#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

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 T is of type char then your compiler should complain of an ambiguity. To indicate that you are specializing a template, as ssteinberg notes, you need to use the template<> 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
月下伊人醉 2024-11-03 17:10:38

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

Try to put template<> in front of Terminallog & operator<<(const char v[]); to tell the compiler that you're specializing a template.

貪欢 2024-11-03 17:10:38

显式专用模板实例化:

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

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

Explicit specialized template instantiation:

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

Something like this. My C++ is rusty.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文