繁体   English   中英

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

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

我有以下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 cout << "Hello, World!" << endl行,我得到以下错误

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

我还可以通过注释掉#include“listtemplate.h”,取消注释hello world line,并在main中包含<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

这实际上是一个有趣的问题。 主要问题是,正如其他人之前指出的那样,您已宣布以下签名:

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

这会触发歧义,因为它是一个包罗万象的模板。 但是为什么编译器可以(明确地)将一个整数插入到cout但是它不能插入一个const char*

原因在于std::basic_ostream模板的定义以及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

现在,当编译器遇到表达式std::cout << 5 ,它发现[1]是非模板化的完美匹配。 它是非模板化的,因为std::coutbasic_ostream类模板的具体实例化的对象,当编译器考虑该类的成员时,类型是固定的。 该方法本身不是模板化的。

模板[3]可以匹配相同的用法,但因为[1]没有模板化,所以它在重载决策中优先,并且没有歧义。

现在,当编译器看到表达式std::cout << "Hello world"; ,它执行查找并找到(在其他无法匹配且因此被丢弃的选项中)选项[2]和[3]。 问题是,现在,两个选项都是模板,第一个可以通过匹配CharT = chartraits = char_traits<char>来解决,而第二个可以通过使T = const char*来匹配(第一个参数是具体的实例化类型)。 编译器无法下定决心(没有部分顺序来定义它应该遵循的选项),并且它会触发歧义错误。

问题中真正有趣的一点是,虽然[1]和[2]两者似乎都在CharTtraits的参数上被模板化,但基本上它们的编译器并没有以相同的方式考虑它们,原因是查找查找[1]作为std::cout的成员,这意味着在[1]中, basic_ostream<char,char_traits<char> >是第一个参数的具体已知类型,并且它是固定的。 模板是类,而不是函数,并且在查找成员函数之前,类实例化类型是固定的。 另一方面,当ADL找到[2]并尝试匹配调用时, basic_ostream<CharT, traits>是一个可以与cout类型匹配的泛型类型

我希望这不会太混乱,但我认为了解类似外观代码的细微差别真是太好了。

我认为问题是在你的标题中你已经建立了这个函数的原型:

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;

编译器无法分辨它应该调用哪个函数 - 标准输出函数或您在列表标题中定义的函数。

声明为:

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

在函数定义中:

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

暂无
暂无

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

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