6 c++ templates operator-overloading
我有以下main.cpp
文件
#include "listtemplate.h"
//#include <iostream>
using namespace std;
int main()
{
int UserChoice;
cout << "Hello, World!" << endl;
cin >> UserChoice;
cout << UserChoice;
}
Run Code Online (Sandbox Code Playgroud)
在它目前的形式,一切正常.我输入一个整数,并将该整数打印到屏幕上.但是,当我取消注释该cout << "Hello, World!" << endl
行时,我收到以下错误
main.cpp:10: error: ambiguous overload for ‘operator<<’ in ‘std::cout << "Hello, World!"’
Run Code Online (Sandbox Code Playgroud)
我也可以通过注释掉#include"listtemplate.h",取消注释hello world line,并包括<iostream>
main(目前可通过模板访问)来使其工作.任何人都能看到我在这里缺少的东西吗?
listtemplate.h
#ifndef LISTTEMPLATE_H
#define LISTTEMPLATE_H
#include "list.h"
using namespace std;
// Default constructor
template <class Type>
list<Type> :: list() : Head(NULL) {}
// Destructor
template <class Type>
list<Type> :: ~list()
{
Node *Temp;
while (Head != NULL)
{
Temp = Head;
Head = Head -> Next;
delete Temp;
}
}
// Copy constructor
template <class Type>
list<Type> :: list (const Type& OriginalList)
{
Node *Marker;
Node *OriginalMarker;
OriginalMarker = OriginalList.Gead;
if (OriginalMarker == NULL) Head = NULL;
else
{
Head = new Node (OriginalMarker -> Element, NULL);
Marker = Head;
OriginalMarker = OriginalMarker -> Next;
while (OriginalMarker != NULL)
{
Marker -> Next = new Node (OriginalMarker -> Next);
OriginalMarker = OriginalMarker -> Next;
Marker = Marker -> Next;
}
}
}
// Copy assignment operator
template <class Type>
list<Type>& list<Type> :: operator= (const list<Type>& Original)
{
Node *Marker;
Node *OriginalMarker;
// Check that we are not assigning a variable to itself
if (this != &Original)
{
// First clear the current list, if any
while (Head != NULL)
{
Marker = Head;
Head = Head -> Next;
delete Marker;
}
// Now build a new copy
OriginalMarker = Original.Head;
if (OriginalMarker == NULL) Head = NULL;
else
{
Head = new Node (OriginalMarker -> Element, NULL);
Marker = Head;
OriginalMarker = OriginalMarker -> Next;
while (OriginalMarker != NULL)
{
Marker -> Next = new Node (OriginalMarker -> Element, NULL);
OriginalMarker = OriginalMarker -> Next;
Marker = Marker -> Next;
}
}
}
return (*this);
}
// Test for emptiness
template <class Type>
bool list<Type> :: Empty() const
{
return (Head == NULL) ? true : false;
}
// Insert new element at beginning
template <class Type>
bool list<Type> :: Insert (const Type& NewElement)
{
Node *NewNode;
NewNode = new Node;
NewNode -> Element = NewElement;
NewNode -> Next = Head;
return true;
}
// Delete an element
template <class Type>
bool list<Type> :: Delete (const Type& DelElement)
{
Node *Temp;
Node *Previous;
// If list is empty
if (Empty()) return false;
// If element to delete is the first one
else if (Head -> Element == DelElement)
{
Temp = Head;
Head = Head -> Next;
delete Temp;
return true;
}
// If the list has only one element which isn't the specified element
else if (Head -> Next == NULL) return false;
// Else, search the list element by element to find the specified element
else
{
Previous = Head;
Temp = Head -> Next;
while ((Temp -> Element != DelElement) && (Temp -> NExt != NULL))
{
Previous = Temp;
Temp = Temp -> Next;
}
if (Temp -> Element == DelElement)
{
Previous -> Next = Temp -> Next;
delete Temp;
return true;
}
else return false;
}
}
// Print the contents of the list
template <class Type>
void list<Type> :: Print (ostream& OutStream) const
{
Node *Temp;
Temp = Head;
while (Temp != NULL)
{
OutStream << Temp -> Element << " ";
Temp = Temp -> Next;
}
}
// Overloaded output operator
template <class Type>
ostream& operator<< (ostream& OutStream, const list<Type>& OutList)
{
OutList.Print (OutStream);
return OutStream;
}
#endif
Run Code Online (Sandbox Code Playgroud)
list.h
#ifndef LIST_H
#define LIST_H
#include <iostream>
#include <cstddef>
using namespace std;
template <class Type>
class list
{
private:
struct Node
{
public:
Type Element;
Node *Next;
Node() : Next(NULL) {} // Default constructor
Node (Type Data, Node *PNode = NULL) : // Non-default constructor
Element (Data),
Next (PNode) {}
};
Node *Head;
public:
list();
~list();
list (const Type& OriginalList);
bool Empty() const;
bool Insert (const Type& NewElement);
bool Delete (const Type& DelElement);
void Print (ostream& OutStream) const;
list& operator= (const list<Type>& Original);
};
template <class Type>
ostream& operator<< (ostream& OutStream, const Type& OutList);
#endif
Run Code Online (Sandbox Code Playgroud)
Dav*_*eas 21
这实际上是一个有趣的问题.主要问题是,正如其他人之前指出的那样,您已宣布以下签名:
template <typename T>
std::ostream& operator<<( std::ostream&, T const & );
Run Code Online (Sandbox Code Playgroud)
这会触发歧义,因为它是一个包罗万象的模板.但是为什么编译器可以(明确地)插入一个整数cout
但是它不能插入const char*
?
原因在于std::basic_ostream
标准中所需的模板和自由函数的定义.特别是,模板类basic_ostream
包含用于插入基本类型的成员函数,包括int
.另一方面,const char*
流入流被定义为模板化自由函数.将三个声明放在一起:
namespace std {
template <typename CharT, typename traits = char_traits<CharT> >
class basic_ostream {
// ...
basic_ostream<CharT,traits>& operator<<(int n); // [1]
// ...
};
template<class charT, class traits> // [2]
basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&, const char*);
}
template <typename T> // [3]
std::ostream& operator<<( std::ostream&, T const & ); // user defined
Run Code Online (Sandbox Code Playgroud)
现在,当编译器遇到表达式时std::cout << 5
,它发现[1]是非模板化的完美匹配.它是非模板化的,因为它是类模板std::cout
的具体实例化的对象basic_ostream
,当编译器考虑该类的成员时,类型是固定的.该方法本身不是模板化的.
模板[3]可以匹配相同的用法,但因为[1]没有模板化,所以它在重载决策中优先,并且没有歧义.
现在,当编译器看到表达式时std::cout << "Hello world";
,它执行查找并找到(在其他无法匹配且因此被丢弃的选项中)选项[2]和[3].的问题是,现在,这两个选项都模板,在第一次通过匹配来解决CharT = char
和traits = char_traits<char>
,而第二个可通过使被匹配T = const char*
(第一个参数是一个具体实例的类型).编译器无法下定决心(没有部分顺序来定义它应该遵循的选项),并且它会触发歧义错误.
在问题真正有趣的一点是,虽然两者[1] [2]似乎在参数作为模板CharT
,并traits
基本上他们没有在编译器相同的方式考虑以同样的方式,之所以说是查找发现[1]作为成员std::cout
,这意味着在[1]中,第一个参数basic_ostream<char,char_traits<char> >
的具体已知类型是固定的.模板是类,而不是函数,并且在查找成员函数之前,类实例化类型是固定的.另一方面,当ADL找到[2]并尝试匹配调用时,basic_ostream<CharT, traits>
是一个可以匹配类型的泛型类型cout
.
我希望这不会太混乱,但我认为了解类似外观代码的细微差别真是太好了.
我认为问题是在你的标题中你已经建立了这个函数的原型:
template <class Type>
ostream& operator<< (ostream& OutStream, const Type& OutList);
Run Code Online (Sandbox Code Playgroud)
而不是这一个:
template <class Type>
ostream& operator<< (ostream& OutStream, const list<Type>& OutList);
Run Code Online (Sandbox Code Playgroud)
你原型的版本说它operator <<
可以打印出任何东西,而不是任何东西的列表.因此,当你写作
cout << "Hello, world!" << endl;
Run Code Online (Sandbox Code Playgroud)
编译器无法分辨它应该调用哪个函数 - 标准输出函数或您在列表标题中定义的函数.