C++ print template container error (error: ambiguous overload for 'operator<<') understanding?

前端 未结 4 551
别跟我提以往
别跟我提以往 2021-01-22 18:35

I want to write template function which can print container like std::vector, std::list.

Below is my function, just overload <<.

相关标签:
4条回答
  • 2021-01-22 18:52

    @miradham explained the issue very well.

    but this a more generic solution using SFINAE to make the overload considered only for types on which the range-based for loop can be used, what ever their template arguments can be.

    type coming form std::basic_string were ignored to prevent ambiguity with the standard operator << to display strings

    c-style array will not be displayed using this overload even though they could because they are decayed to pointers and displayed with the standard operator <<

    #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;
    }
    
    0 讨论(0)
  • 2021-01-22 19:02

    Declaring template<typename Container> could be dangerous as this template includes 'all' variable types int, char etc. Due to this compiler does not know which operator<< to use.

    In order to take only container type variables use template of templates. Here is working code for you

    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;
    }
    
    0 讨论(0)
  • 2021-01-22 19:08

    The problem is that the templated type Container can match any type, not just containers. That includes the " " you are trying to print.

    If you look at the error message from a different compiler: https://godbolt.org/g/3YKtca

    <source>:5:15: note: candidate function [with Container = char [2]]
    
    std::ostream& operator<<(std::ostream& out, const Container& c){
    

    Perhaps you want a partial specialization of vector<T> to only take vectors. Determining if a type is a container is a more complicated problem.

    #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

    0 讨论(0)
  • 2021-01-22 19:10

    The problem is that operator<< that you defined matches for both std::vector and const char (&array)[N] (the type of " " that you try to stream to out).

    A simplified code example that demonstrates the problem:

    #include <iostream>
    
    template<typename Container>
    std::ostream& operator<<(std::ostream& out, const Container& c)
    {
        return out;
    }
    
    int main()
    {
        std::cout<<" "<<std::endl;
        return 0;
    }
    

    The following example would restrict operator<< to std::vectors only:

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

    Live example.

    0 讨论(0)
提交回复
热议问题