Неоднозначная перегрузка для ‘operator <<’ в ‘std :: cout << - PullRequest
6 голосов
/ 16 марта 2011

У меня есть следующий main.cpp файл

#include "listtemplate.h"
//#include <iostream>
using namespace std;

int main()
{
    int UserChoice;
    cout << "Hello, World!" << endl;
    cin >> UserChoice;
    cout << UserChoice;
}

В его текущей форме все работает.Я ввожу целое число, и это целое число выводится на экран.Однако, когда я раскомментирую строку cout << "Hello, World!" << endl, я получаю следующую ошибку

main.cpp:10: error: ambiguous overload for ‘operator<<’ in ‘std::cout << "Hello, World!"’

. Я также могу заставить ее работать, закомментировав #include "listtemplate.h", раскомментировав строку hello world и включив<iostream> в основном (в настоящее время доступен через шаблон. Кто-нибудь может увидеть, что мне здесь не хватает?

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

Ответы [ 3 ]

21 голосов
/ 16 марта 2011

Это на самом деле интересный вопрос.Основная проблема, как уже указывали другие, заключается в том, что вы объявили следующую подпись:

template <typename T>
std::ostream& operator<<( std::ostream&, T const & );

И это вызывает неоднозначность, поскольку это шаблон catch-all .Но почему компилятор может (однозначно) вставить целое число в cout, но не может вставить const char*?

Причина этого заключается в определении шаблона std::basic_ostream и свободных функцийчто требуется в стандарте.В частности, шаблон класса basic_ostream содержит member функции для вставки основных типов, включая 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

Теперь, когда компилятор встречает выражение 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.

* 1043.* Я надеюсь, что это не слишком запутанно, но я думаю, что приятно знать тонкую разницу в аналогично выглядящем коде.
9 голосов
/ 16 марта 2011

Я думаю, что проблема в том, что в своем заголовке вы создали прототип этой функции:

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

вместо этой:

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

Версия, которую вы создали в прототипечто это operator <<, который может распечатать что угодно , а не списки чего-либо.Следовательно, когда вы пишете

cout << "Hello, world!" << endl;

Компилятор не может сказать, какую функцию он должен вызывать - стандартную функцию вывода или функцию, которую вы определили в заголовке списка.

1 голос
/ 16 марта 2011

объявлено как:

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

в определении функции как:

ostream& operator<< (ostream& OutStream, const list<Type>& OutList)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...