I have the following main.cpp
file
#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
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?
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
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
This is in fact an interesting question. The main issue is, as others have pointed before that you have declared the following signature:
And that triggers the ambiguity, as it is a catch-all template. But why is it that the compiler can insert (unambiguously) an integer into
cout
but it cannot insert aconst 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 classbasic_ostream
contains member functions to insert basic types, includingint
. On the other hand, the insertion ofconst char*
into streams is defined as a templated free function. Bringing the three declarations together:Now, when the compiler encounters the expression
std::cout << 5
, it finds that [1] is a non-templated perfect match. It is non-templated asstd::cout
is an object of a concrete instantiation of thebasic_ostream
class template, when the compiler considers the members of that class, the type is fixed. The method itself is not templated.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.
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 matchingCharT = char
andtraits = char_traits<char>
, while the second can be matched by makingT = 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.The really interesting point in the question is that while both [1] and [2] seem to be templated on the arguments
CharT
andtraits
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 ofstd::cout
, that means that in [1],basic_ostream<char,char_traits<char> >
is the concrete known type 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 type that can be matched to the type ofcout
.I hope this is not too confusing, but I think it is nice to know the subtle difference of similarly looking code.
declared as:
in the function definition as:
I think that the problem is that in your header you've prototyped this function:
instead of this one:
The version you've prototyped says that it's an
operator <<
that can print out anything, not lists of anything. Consequently, when you writeThe 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.