C++ “std::cout <<”中“operator<<”的不明确重载
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5319190/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Ambiguous overload for ‘operator<<’ in ‘std::cout <<
提问by templatetypedef
I have the following main.cpp
file
我有以下main.cpp
文件
#include "listtemplate.h"
//#include <iostream>
using namespace std;
int main()
{
int UserChoice;
cout << "Hello, World!" << endl;
cin >> UserChoice;
cout << UserChoice;
}
In it's current form, everything works. I enter an integer, and that integer is printed to the screen. However, when I uncomment the cout << "Hello, World!" << endl
line, I get the following error
以目前的形式,一切正常。我输入一个整数,该整数被打印到屏幕上。但是,当我取消注释该cout << "Hello, World!" << endl
行时,出现以下错误
main.cpp:10: error: ambiguous overload for ‘operator<<' in ‘std::cout << "Hello, World!"'
I can also make it work by commenting out #include "listtemplate.h", uncommenting the hello world line, and including <iostream>
in main (currently accessible through the template. Can anyone see what I'm missing here?
我还可以通过注释掉 #include "listtemplate.h"、取消注释 hello world 行并包含<iostream>
在 main (当前可通过模板访问。有人能看到我在这里遗漏了什么吗?
listtemplate.h
列表模板.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
list.h
列表.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
回答by David Rodríguez - dribeas
This is in fact an interesting question. The main issue is, as others have pointed before that you have declared the following signature:
这实际上是一个有趣的问题。主要问题是,正如其他人之前指出的那样,您已经声明了以下签名:
template <typename T>
std::ostream& operator<<( std::ostream&, T const & );
And that triggers the ambiguity, as it is a catch-alltemplate. But why is it that the compiler can insert (unambiguously) an integer into cout
but it cannot insert a const char*
?
这会引发歧义,因为它是一个包罗万象的模板。但是为什么编译器可以(明确地)插入一个整数cout
而不能插入一个const char*
?
The reason for that is in the definition of the std::basic_ostream
template and free functions that are required in the standard. In particular, the template class basic_ostream
contains memberfunctions to insert basic types, including int
. On the other hand, the insertion of const char*
into streams is defined as a templated free function. Bringing the three declarations together:
原因在于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
Now, when the compiler encounters the expression std::cout << 5
, it finds that [1] is a non-templated perfect match. It is non-templated as std::cout
is an object of a concrete instantiation of the basic_ostream
class template, when the compiler considers the members of that class, the type is fixed. The method itself is not templated.
现在,当编译器遇到表达式 时std::cout << 5
,它发现 [1] 是非模板化的完美匹配。它是非模板std::cout
化的,因为是basic_ostream
类模板的具体实例化的对象,当编译器考虑该类的成员时,类型是固定的。方法本身不是模板化的。
The template [3] could match the same use, but because [1] is not templated it takes precedence in the overload resolution, and there is no ambiguity.
模板 [3] 可以匹配相同的用法,但由于 [1] 未模板化,因此在重载解析中优先,并且没有歧义。
Now, when the compiler sees the expression std::cout << "Hello world";
, it performs the lookup and it finds (among other options that cannot be matched and are thus discarded) options [2] and [3]. The problem is that now, both options are templates, the first one can be resolved by matching CharT = char
and traits = char_traits<char>
, while the second can be matched by making T = const char*
(the first argument is a concrete instantiated type). The compiler cannot make up its mind (there is no partial order that defines which option it should follow), and it triggers the ambiguity error.
现在,当编译器看到表达式时std::cout << "Hello world";
,它会执行查找并找到(在其他无法匹配并因此被丢弃的选项中)选项 [2] 和 [3]。现在的问题是,这两个选项都是模板,第一个可以通过匹配CharT = char
and解决traits = char_traits<char>
,而第二个可以通过 make 匹配T = const char*
(第一个参数是一个具体的实例化类型)。编译器无法下定决心(没有偏序定义它应该遵循哪个选项),并触发歧义错误。
The really interesting point in the question is that while both [1] and [2] seem to be templated on the arguments CharT
and traits
basically in the same way they are not considered in the same way by the compiler, the reason for that is that lookup finds [1] as a member of std::cout
, that means that in [1], basic_ostream<char,char_traits<char> >
is the concrete knowntype of the first argument and it is fixed. The template is the class, not the function, and the class instantiation types are fixed before lookup considers the member functions. On the other hand, when it ADL finds [2] and tries to match against the call, basic_ostream<CharT, traits>
is a generic typethat can be matched to the type of cout
.
问题中真正有趣的一点是,虽然 [1] 和 [2] 似乎都对参数进行了模板化,CharT
并且traits
基本上以相同的方式被编译器以相同的方式考虑,其原因是查找发现 [1] 作为 的成员std::cout
,这意味着在 [1] 中,basic_ostream<char,char_traits<char> >
是第一个参数的具体已知类型并且它是固定的。模板是类,而不是函数,在查找考虑成员函数之前,类实例化类型是固定的。在另一方面,当它认定ADL [2]和尝试匹配针对该呼叫,basic_ostream<CharT, traits>
是一种通用类型,可以被匹配到的类型cout
。
I hope this is not too confusing, but I think it is nice to know the subtle difference of similarlylooking code.
我希望这不会太令人困惑,但我认为了解类似代码之间的细微差别是件好事。
回答by templatetypedef
I think that the problem is that in your header you've prototyped this function:
我认为问题在于在您的标题中您已经对这个函数进行了原型设计:
template <class Type>
ostream& operator<< (ostream& OutStream, const Type& OutList);
instead of this one:
而不是这个:
template <class Type>
ostream& operator<< (ostream& OutStream, const list<Type>& OutList);
The version you've prototyped says that it's an operator <<
that can print out anything, not lists of anything. Consequently, when you write
您制作原型的版本说它operator <<
可以打印任何内容,而不是任何内容的列表。因此,当你写
cout << "Hello, world!" << endl;
The compiler can't tell which function it's supposed to call - the standard output function or the one you've defined in your list header.
编译器无法判断它应该调用哪个函数 - 标准输出函数还是您在列表标题中定义的函数。
回答by jonsca
declared as:
声明为:
ostream& operator<< (ostream& OutStream, const Type& OutList);
in the function definition as:
在函数定义中:
ostream& operator<< (ostream& OutStream, const list<Type>& OutList)