Search code examples
c++vectoriteratorostream

ostream_iterator for a vector<int> does not compile. Why?


Please see the following minimum reproducable example:

#include <iostream>
#include <vector>
#include <algorithm>

// Define inserter operator for std::vector<int>
std::ostream& operator << (std::ostream& os, const std::vector<int>& v) {
    std::copy(v.begin(), v.end(), std::ostream_iterator<int>(os, " "));
    return os;
}
// Define inserter operator for std::vector<std::vector<int>>
std::ostream& operator << (std::ostream& os, const std::vector<std::vector<int>>& vv) {
    
    // ******* Error in this line *****
    std::copy(vv.begin(), vv.end(), std::ostream_iterator<std::vector<int>>(os,"\n"));

    // OK. The following works and will call the operator above
    for (const std::vector<int>& v : vv) os << v << '\n';
    return os;
}
int main() {
    std::vector<std::vector<int>> vvi{ {1,2}, {3,4,5}, {6,7,8,9 } };
    std::cout << vvi;
    return 0;
}

The std::ostream_iterator<std::vector<int>> will not compile, compiler says:

binary '<<': no operator found which takes a right-hand operand of type 'const _Ty' (or there is no acceptable conversion)

Although an inserter operator for std::vector<int> is available.

if I instead use:

for (const std::vector<int>& v : vv) os << v << '\n';

The insert operator for std::vector<int> will be called. But not in the case of the std::ostream_iterator.

CppReference does not help me.

Why does this not compile?


Solution

  • The first problem is that you're missing an

    #include <iterator>
    

    You indicate that you're already familiar with cppreference.com, so you should take note that std::ostream_iterator is noted as requiring this header file.

    But the real reason for the compilation failure is that the overloaded operator is invoked by std::copy which, naturally, is in the std namespace. Since the code that invokes the << operator is in the std namespace, overload resolution searches the std namespace for the defined overloads of <<, and finds a whole bunch of them, for various native types. All of those overload resolutions fail. None of them are for std::vector<int>. The global namespace is not searched, to find your custom << overload, since at least one << overload is found in the std namespace. Game over.

    If there were no defined << overloads in the std namespace, then overload resolution will eventually find your custom overload. Alas, that is not the case.

    One way to make the compilation error go away is to put your overload into the std namespace:

    namespace std {
        // Define inserter operator for std::vector<int>
        std::ostream& operator << (std::ostream& os, const std::vector<int>& v) {
            std::copy(v.begin(), v.end(), std::ostream_iterator<int>(os, " "));
            return os;
        }
    }
    

    However, this is pedantic undefined behavior. In any case, this is the reason for the compilation error: the existing overloads for << are searched in the std namespace first, because they are invoked from within std::copy, and existing overload resolutions are found first, and so other potential overloads in other namespaces are not considered.