【问题标题】:C++ print template container error (error: ambiguous overload for 'operator<<') understanding?C++打印模板容器错误(错误:'operator<<'的模糊重载)理解?
【发布时间】:2018-07-26 05:20:35
【问题描述】:

我想编写可以打印容器的模板函数,如std::vector,std::list。

下面是我的函数,只是重载&lt;&lt;

template<typename Container>
std::ostream& operator<<(std::ostream& out, const Container& c){
    for(auto item:c){
        out<<item;
    }
    return out;
}

测试代码如下:

int main(){
    std::vector<int> iVec{5, 9, 1, 4, 6};
    std::cout<<iVec<<std::endl;
    return 0;
}

输出:

59146

并且我想在每个值中添加一个空格字符串(输出类似于5 9 1 4 6),所以我将函数更改为:

template<typename Container>
std::ostream& operator<<(std::ostream& out, const Container& c){
    for(auto item:c){
        out<<item<<" ";
    }
    return out;
}

然后报错:

merror: ambiguous overload for 'operator<<' (operand types are 'std::basic_ostream<char>' and 'const char [2]')
         out<<item<<" ";

我知道&lt;&lt; 可以输出普通类型like。

int a = 0;
double f = 0.3;
std::string s = "123";
std::cout<<a<<f<<s<<std::endl;

那么为什么会出现上述错误?有什么办法可以解决吗?

我看到了这个问题Ambiguous overload for ‘operator<<’ in ‘std::cout <<,但还是看不懂。

所有代码:

#include <iostream>
#include <vector>

template<typename Container>
std::ostream& operator<<(std::ostream& out, const Container& c){
    for(auto item:c){
        out<<item;
        // out<<item<<" "; // error
    }
    return out;
}

int main(){
    std::vector<int> iVec{5, 9, 1, 4, 6};
    std::cout<<iVec<<std::endl;
    return 0;
}

【问题讨论】:

  • 请发帖minimal reproducible example。谢谢。
  • 请勿粘贴文字图片。
  • 您链接的问答中的答案 2 非常有效。问题是const Container&amp; c 太宽泛了。它可以是任何东西。一个容器,一个字符串,一个整数,一个袋熊......你的名字。你会得到一个模板化的&lt;&lt;,它可以替换所有&lt;&lt; 重载,所以可怜的编译器不知道它应该使用哪个&lt;&lt;,你的或者打算与char 数组一起使用的那个。
  • @PaulSanders ,我已经更新了所有代码。
  • 说实话,这张照片并没有那么可恶。图像的问题是它们比文本更难搜索,对视障者不透明,经常被防火墙阻止,链接失效,无法编译,最糟糕的是,用来代替实际的问题解释。您的图片是补充信息,是额外的。

标签: c++ c++11


【解决方案1】:

声明template&lt;typename Container&gt; 可能很危险,因为此模板包含“所有”变量类型intchar 等。由于此编译器不知道要使用哪个operator&lt;&lt;

为了仅采用容器类型变量,请使用模板模板。 这是适合您的工作代码

template<typename T, template <typename, typename> class Container>
std::ostream& operator<<(std::ostream& out, const Container<T, std::allocator<T>>& c) {
    for (auto item : c) {
        out << item << " ";
    } 
    return out;
}

int main()
{
    cout << "Hello world" << endl;
    int arr[] = { 0,3,6,7 };
    vector<int> v(arr, arr+4);
    cout << v << endl;
    return 0;
}

【讨论】:

  • 这对于不遵循该模式的容器来说变得非常时髦。完全通用实际上很难做到。
  • 这个很好用,也是我要找的,但还是看不清楚。
  • 是的,template &lt;typename Container&gt; 将匹配 int,但与 const char* 不同的是,它并不模棱两可(参见 @Jayhello 的 OP 中的工作代码)。正如@user4581301 所指出的,this 的答案是“砰砰”。
【解决方案2】:

@miradham 很好地解释了这个问题。

但这是一个更通用的解决方案,使用 SFINAE 使重载仅适用于可以使用基于范围的 for 循环的类型,无论它们的模板参数是什么。

忽略来自 std::basic_string 的类型,以防止与标准 operator &lt;&lt; 显示字符串产生歧义

c 样式数组不会使用此重载显示,即使它们可以显示,因为它们已衰减为指针并使用标准 operator &lt;&lt; 显示

#include <iostream>
#include <vector>
#include <type_traits>
#include <array>
#include <string>

template<template<typename...> typename From, typename T>
struct is_from : std::false_type {};

template<template<typename...> typename From, typename ... Ts>
struct is_from<From, From<Ts...> > : std::true_type {};

template <typename...>
using void_t = void;

template <typename T, typename = void>
struct is_input_iterator : std::false_type { };

template <typename T>
struct is_input_iterator<T,
    void_t<decltype(++std::declval<T&>()),
           decltype(*std::declval<T&>()),
           decltype(std::declval<T&>() == std::declval<T&>())>>
    : std::true_type { };

template<typename Container, 
typename std::enable_if<is_input_iterator<decltype(std::begin(std::declval<Container>()))>::value &&
                        is_input_iterator<decltype(std::end(std::declval<Container>()))>::value &&
                        !is_from<std::basic_string, Container>::value, int>::type = 0>
std::ostream& operator<<(std::ostream& out, const Container& c){
    for(const auto& item:c){
        out << item << " ";
    }
    return out;
}

int main(){

    std::array<int, 6> arr{0, 1, 2, 3, 4, 5};
    std::vector<int> vec{5, 9, 1, 4, 6};

    std::cout << vec << std::endl;
    std::cout << arr << std::endl;
    std::cout << std::string("test") << std::endl;
    return 0;
}

【讨论】:

  • 不能在 MSVC 2017 中编译。
【解决方案3】:

问题在于模板化类型 Container 可以匹配任何类型,而不仅仅是容器。这包括您尝试打印的" "

如果您查看来自不同编译器的错误消息:https://godbolt.org/g/3YKtca

<source>:5:15: note: candidate function [with Container = char [2]]

std::ostream& operator<<(std::ostream& out, const Container& c){

也许您希望 vector&lt;T&gt; 的部分特化只接受向量。确定一个类型是否是一个容器是一个更复杂的问题。

#include <iostream>
#include <vector>

template<typename E, typename A>
std::ostream& operator<<(std::ostream& out, const std::vector<E, A>& c){
    for(auto item:c){
        out<<item;
        out<<item<<" "; // error
    }
    return out;
}

int main(){
    std::vector<int> iVec{5, 9, 1, 4, 6};
    std::cout<<iVec<<std::endl;
    return 0;
}

https://godbolt.org/g/NJNwmN

【讨论】:

  • @user4581301 向量的部分特化可能是完全通用和向量的显式特化之间的中间值(回复原始评论)
  • 同意。我在考虑最简单的方法,以使提问者更容易,但您只是简单地构建了更复杂的选项。有时很难做到。
【解决方案4】:

问题是您定义的operator&lt;&lt; 匹配std::vectorconst char (&amp;array)[N](您尝试流式传输到out" " 的类型)。

演示问题的简化代码示例:

#include <iostream>

template<typename Container>
std::ostream& operator<<(std::ostream& out, const Container& c)
{
    return out;
}

int main()
{
    std::cout<<" "<<std::endl;
    return 0;
}

以下示例将 operator&lt;&lt; 仅限于 std::vectors:

template<typename ... Args>
std::ostream& operator<<(std::ostream& out, const std::vector<Args...>& c)
{
    for(auto item:c){
        out<<item<<" ";
    }
    return out;
}

Live example.

【讨论】:

    猜你喜欢
    • 2021-06-15
    • 1970-01-01
    • 1970-01-01
    • 2019-04-13
    • 1970-01-01
    • 2013-09-12
    • 1970-01-01
    • 2019-01-25
    • 1970-01-01
    相关资源
    最近更新 更多