C++ STL 容器的 operator<< 的泛型重载会对字符串产生不明确的重载错误

C++ generic overload of operator<< for STL containers produces ambiguous overload error with strings

提问人:sancho.s ReinstateMonicaCellio 提问时间:10/19/2023 最后编辑:sancho.s ReinstateMonicaCellio 更新时间:10/31/2023 访问量:141

问:

我的意思是编写一个通用重载来打印 STL 容器。 我把下面的代码放在一起。 每当涉及 时,它都会产生编译错误,在问题行 1 和 2 中的示例中。operator<<operator<<stringambiguous overload for 'operator<<'

我怎样才能在不丢失泛型重载的情况下摆脱错误,也不必为每种可能的字符串容器使用编写显式实例化?也许从我的重载中排除了字符串。

#include <iostream>
#include <vector>
#include <set>
#include <list>
#include <map>
#include <tuple>
#include <string>

// Maximum number of printed values. After this, print "..."
#define MAX_PRINT_VALS 10

//=========================================================================
// Set of functions to dump STL containers

template <template <class...> class Container, class ...T>
std::ostream& operator<<(std::ostream& os, const Container<T...>& c)
{
    os << "[";

    size_t nvals = 0;
    for ( auto iter = c.begin() ; iter != c.end() ; iter++ ) {
        os << *iter;
        nvals++;
        if (iter != --c.end())
            os << ", ";
        if (nvals > MAX_PRINT_VALS) {
           os << "... (total of " << c.size() << " values)";
           break;
        }
    }

    os << "]";
    return os;
}

template<class Key, class T>
std::ostream& operator<<(std::ostream& os, const std::pair<Key, T>& p)
{
    os << "(" << p.first << ", " << p.second << ")";
    //os << std::endl;
    return os;
}


using namespace std;


int main(int argc, char **argv) {

    //============================================================
    // Print vector
    const size_t nmax = 3;
    vector<double const*> vec_dp;
    for (size_t n = 0; n < nmax; n++) {
        vec_dp.push_back(new double(n+1.5));
    }
    cout << "Vector of indices vec_dp = " << vec_dp << endl;
    for (size_t n = 0; n < nmax; n++) {
        delete vec_dp[n];
    }

    vector<string> vec_s;
    for (size_t n = 0; n < nmax; n++) {
        vec_s.push_back("asa");
    }
    cout << "Vector of string vec_s = " << vec_s << endl;         // PROBLEM LINE 1

    //============================================================
    // Print set
    set<double> set_d;
    for (size_t n = 0; n < nmax; n++) {
        set_d.insert(n+1.3);
    }
    cout << "Set of double set_d = " << set_d << endl;

    //============================================================
    // Print list
    list<double> list_d;
    for (size_t n = 0; n < (nmax + 10); n++) {
        list_d.emplace_back(n+1.4);
    }
    cout << "List of double list_d = " << list_d << endl;

    //============================================================
    // Print map
    typedef pair<int, int> pair2_t;
    map<pair2_t::first_type, pair2_t::second_type> map_i_i;
    for (size_t n = 0; n < (nmax + 10); n++) {
        map_i_i.insert(pair2_t(n+1, n+2));
    }
    cout << "Map of (int, int) map_i_i = " << map_i_i << endl;

    typedef pair<int, string> pair1_t;
    map<pair1_t::first_type, pair1_t::second_type> map_i_s;
    for (size_t n = 0; n < (nmax + 10); n++) {
        map_i_s.insert(pair1_t(n+1, "one"));
    }
    cout << "Map of (int, string) map_i_s = " << map_i_s << endl;         // PROBLEM LINE 2


    return 0;
}

相关

  1. C++打印模板容器错误(错误:'operator<<' 的模棱两可的重载)理解?
  2. “std::cout <<中”operator<<“的不明确重载
  3. 重载 << C++ STL 容器的运算符
  4. 缩小 C++ 概念范围以排除某些类型
C++ 字符串 stl 17 运算符重载 c++20

评论

2赞 273K 10/19/2023
std::string也是一个容器。
0赞 sancho.s ReinstateMonicaCellio 10/19/2023
@273K - 我知道。有什么可能的解决方案吗?也许从我的重载中排除字符串(我不知道这是否可能)?
0赞 273K 10/19/2023
是的,这是可能的。请标记使用的 C++ 版本。
1赞 Jarod42 10/19/2023
为你不拥有的类型提供(泛型)运算符是一个坏主意。当/如果类型在更高版本中提供该运算符时,可能会遇到 ODR 冲突。不过,将其包装在您自己的流中可能没问题。
1赞 n. m. could be an AI 10/20/2023
@Jarod42不是问题,请将您的运算符放在命名空间中并放入一些.using

答:

3赞 user12002570 10/19/2023 #1

我怎样才能在不丢失泛型重载的情况下摆脱错误,也不必为每种可能的字符串容器使用编写显式实例化?也许从我的重载中排除字符串

您可以使用(因为您使用的是 )来排除您自己的重载,如下所示:std::enable_ifstd::string

template <template <class... K> class Container, class ...T   >  
//added this to make use of SFINAE
std::enable_if_t<not (std::is_same_v<std::string, Container<T...>>),std::ostream&>                     operator<<(std::ostream& os, const Container<T...>& c)   
{                  
    //other code as before...

    os << "]";
    return os;
}
//other code as before...
int main(int argc, char **argv) {

     //other code as before...
     cout << "Vector of string vec_s = " << vec_s << endl; //works now    
} 

工作演示


使用 ,您可以改用甚至可以使代码更具可读性。演示requires

评论

0赞 sancho.s ReinstateMonicaCellio 10/20/2023
C++17 版本适用于我迄今为止测试过的每个案例,除了一个:.我不得不为它单独写一个。出现了几个小问题:1.我必须用in替换,以消除编译错误,为什么?2. 我不能用代替为什么?3. 有没有办法包含在我的通用重载中?array<T, n>operator<<--a.end()(a.end() - 1)array<T, n>::operator<<lvalue required as decrement operandfor ( const auto & iter...for ( auto iter...array<T, n>::operator<<
0赞 user12002570 10/20/2023
@sancho.s恢复MonicaCellio 随意为后续问题(与此原始问题无关)创建/提出新的单独问题,因为评论部分有字数限制和格式问题。另外,我已经看到评论在没有任何警告的情况下被多次删除,因此应避免在评论框中写下答案(用于后续问题)。
0赞 James Kanze 10/31/2023
这仍然允许他的操作员拾取除 以外的任何类,即使它不是容器。这可以通过添加额外的来避免 - 如果我只想要一个容器,我通常会使用 on 存在成员函数 ,尽管这并不是真正的 100%。std::stringenable_ifenable_ifbegin