简体   繁体   English

'std :: cout <中'operator <<'的模糊重载

[英]Ambiguous overload for ‘operator<<’ in ‘std::cout <<

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 但是,当我取消注释cout << "Hello, World!" << endl 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 line,并在main中包含<iostream> (目前可通过模板访问)来使其工作。任何人都能看到我在这里缺少的东西吗?

listtemplate.h 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 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: 主要问题是,正如其他人之前指出的那样,您已宣布以下签名:

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* ? 但是为什么编译器可以(明确地)将一个整数插入到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. 原因在于std::basic_ostream模板的定义以及std::basic_ostream中所需的自由函数。 In particular, the template class basic_ostream contains member functions to insert basic types, including int . 特别是,模板类basic_ostream包含用于插入基本类型的成员函数,包括int On the other hand, the insertion of const char* into streams is defined as a templated free function. 另一方面,将const char*插入流中定义为模板化自由函数。 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. 现在,当编译器遇到表达式std::cout << 5 ,它发现[1]是非模板化的完美匹配。 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. 它是非模板化的,因为std::coutbasic_ostream类模板的具体实例化的对象,当编译器考虑该类的成员时,类型是固定的。 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. 模板[3]可以匹配相同的用法,但因为[1]没有模板化,所以它在重载决策中优先,并且没有歧义。

Now, when the compiler sees the expression std::cout << "Hello world"; 现在,当编译器看到表达式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]. ,它执行查找并找到(在其他无法匹配且因此被丢弃的选项中)选项[2]和[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). 问题是,现在,两个选项都是模板,第一个可以通过匹配CharT = chartraits = char_traits<char>来解决,而第二个可以通过使T = const char*来匹配(第一个参数是具体的实例化类型)。 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. 问题中真正有趣的一点是,虽然[1]和[2]两者似乎都在CharTtraits的参数上被模板化,但基本上它们的编译器并没有以相同的方式考虑它们,原因是查找查找[1]作为std::cout的成员,这意味着在[1]中, basic_ostream<char,char_traits<char> >是第一个参数的具体已知类型,并且它是固定的。 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 . 另一方面,当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 similarly looking code. 我希望这不会太混乱,但我认为了解类似外观代码的细微差别真是太好了。

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. 你原型化的版本说它是一个operator <<可以打印出任何东西 ,而不是任何列表。 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. 编译器无法分辨它应该调用哪个函数 - 标准输出函数或您在列表标题中定义的函数。

declared as: 声明为:

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

in the function definition as: 在函数定义中:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM