Ambiguous overload for ‘operator<<’ in ‘std:

2020-04-05 08:51发布

问题:

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

回答1:

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

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.



回答2:

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-all template. But why is it that the compiler can insert (unambiguously) an integer into cout but it cannot insert a 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 member functions 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:

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.

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

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 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 of cout.

I hope this is not too confusing, but I think it is nice to know the subtle difference of similarly looking code.



回答3:

declared as:

ostream& operator<< (ostream& OutStream, const Type& OutList);

in the function definition as:

ostream& operator<< (ostream& OutStream, const list<Type>& OutList)