如何编写插入运算符函数模板?

How to write an insertion operator function template?

我正在尝试为插入运算符编写单个函数模板而不是一堆类似的重载。冗余的重载版本有效,但是当我尝试将它们组合在一个函数模板中时,编译器抱怨有歧义。例如:


#include <iostream>
#include <list>

class fooBar
{
public:
    fooBar(int iVal): iValue(iVal) {}
    int getValue() {return iValue;}
    
private:
    int iValue;

};

class foo
{
public:
    foo()
    {
        for(int i = 0; i < 64; i++)
            lstFooBars.push_back(fooBar(i));
    }
    std::list<fooBar>& getList()
    {
        return lstFooBars;
    }

private:
    std::list<fooBar> lstFooBars;
    
};

class bar
{
public:
    bar()
    {
        for(int i = 0; i < 64; i++)
            lstFooBars.push_back(fooBar(i));
    }
    std::list<fooBar>& getList()
    {
        return lstFooBars;
    }

private:
    std::list<fooBar> lstFooBars;
    
};

std::ostream& operator<<(std::ostream& osOut, fooBar& fbrFooBar)
{
    osOut << fbrFooBar.getValue();

    return osOut;
}

template <typename T> std::ostream& operator<<(std::ostream& osOut, T& tContainer)
{
    for(fooBar fbrFooBar: tContainer.getList())
        osOut << "[" << fbrFooBar << "] ";

    return osOut;
}

int main()
{
    foo fooFoo;
    bar barBar;

    std::cout << std::endl << fooFoo << std::endl << std::endl;
    std::cout << std::endl << barBar << std::endl << std::endl;

    return 0;
}

...编译器告诉我:

test.cpp: In function ‘std::ostream& operator<<(std::ostream&, T&)’:
test.cpp:63:9: error: ambiguous overload for ‘operator<<’ (operand types are ‘std::ostream’ {aka ‘std::basic_ostream<char>’} and ‘const char [2]’)
   63 |   osOut << "[" << fbrFooBar << "] ";
      |   ~~~~~ ^~ ~~~
      |   |        |
      |   |        const char [2]
      |   std::ostream {aka std::basic_ostream<char>}

为什么当你为每个案例一遍又一遍地重载相同的函数时它会工作并且它不会像这样编译?我在这里错过了什么?

您无意中为 const char* 添加了一个可能的重载:

template<typename T>
std::ostream& operator<<(std::ostream& osOut, T& tContainer)

如果使用 SFINAE 缩小范围,它应该可以工作。

此重载仅适用于具有 getList() 成员函数的类型,例如:

template<typename T, typename U = decltype(std::declval<T>().getList())>
std::ostream& operator<<(std::ostream& osOut, T& tContainer)

operator<< 默认情况下将 chars 作为参数,而不是字符串文字(在 "s 内)https://www.cplusplus.com/reference/ostream/ostream/operator-free/.

因此,为了使您提供的代码中的调用不含糊,您应该尝试使用单个 charstd::string:

#include <iostream>
#include <list>
#include <string>

class fooBar
{
public:
    fooBar(int iVal): iValue(iVal) {}
    int getValue() {return iValue;}

private:
    int iValue;

};

class foo
{
public:
    foo()
    {
        for(int i = 0; i < 64; i++)
            lstFooBars.push_back(fooBar(i));
    }
    std::list<fooBar>& getList()
    {
        return lstFooBars;
    }

private:
    std::list<fooBar> lstFooBars;

};

class bar
{
public:
    bar()
    {
        for(int i = 0; i < 64; i++)
            lstFooBars.push_back(fooBar(i));
    }
    std::list<fooBar>& getList()
    {
        return lstFooBars;
    }

private:
    std::list<fooBar> lstFooBars;

};

std::ostream& operator<<(std::ostream& osOut, fooBar& fbrFooBar)
{
    osOut << fbrFooBar.getValue();

    return osOut;
}

template <typename T> std::ostream& operator<<(std::ostream& osOut, T& tContainer)
{
    for(fooBar fbrFooBar: tContainer.getList())
        //osOut << std::string("[") << fbrFooBar << std::string("] ");  // solution 1: use std::string
        osOut << '[' << fbrFooBar << ']' << ' ';                        // solution 2: use single chars

    return osOut;
}

int main()
{
    foo fooFoo;
    bar barBar;

    std::cout << std::endl << fooFoo << std::endl << std::endl;
    std::cout << std::endl << barBar << std::endl << std::endl;

    return 0;
}