3

I am trying to understand why the following code doesn't compile

#include <vector>
#include <ostream>
#include <iterator>
#include <iostream>

template <class ostream>
ostream& operator<<(ostream& o, const std::vector<double>& data)
{
    std::copy(data.begin(), data.end(), std::ostream_iterator<double>(o, " "));
    return o;
}

template<class ostream>
ostream& operator<<(ostream& o, const std::vector<std::vector<double>>& data)
{
    std::copy(data.begin(), data.end(), std::ostream_iterator<std::vector<double>>(o, "\n"));
    return o;
}

int main(int argc, char **argv)
{
    std::vector<std::vector<double>> vecvec = {{1,2,3}, 
                                               {4,5,6}};
    std::cout << vecvec << std::endl;
}

I thought that since I have defined an operator<< for vector<double> I should be able to take advantage of the ostream_iterator

Instead I get a compilation error, and if I change the code to the following, then everything compiles fine.

#include <vector>
#include <ostream>
#include <iterator>
#include <iostream>

template <class ostream>
ostream& operator<<(ostream& o, const std::vector<double>& data)
{
    std::copy(data.begin(), data.end(), std::ostream_iterator<double>(o, " "));
    return o;
}

template<class ostream>
ostream& operator<<(ostream& o, const std::vector<std::vector<double>>& data)
{
    /** changed to manually looping **/
    for (const auto& line : data)
    {
        o << line << "\n";
    }
    return o;
}

int main(int argc, char **argv)
{
    std::vector<std::vector<double>> vecvec = {{1,2,3}, 
                                               {4,5,6}};
    std::cout << vecvec << std::endl;
}

What am I doing wrong?

Most importantly... could anyone explain to me why ostream_iterator fails to compile here?

I can find a workaround and fix my problem, but it seems that I have not fully understood how the ostream_iterator works

Here is the output of the compiler (gcc 4.8.5)

In file included from /opt/compiler-explorer/gcc-4.8.5/include/c++/4.8.5/iterator:66:0,from <source>:3:

/opt/compiler-explorer/gcc-4.8.5/include/c++/4.8.5/bits/stream_iterator.h: In instantiation of 'std::ostream_iterator<_Tp, _CharT, _Traits>& std::ostream_iterator<_Tp, _CharT, _Traits>::operator=(const _Tp&) [with _Tp = std::vector<double>; _CharT = char; _Traits = std::char_traits<char>]':

/opt/compiler-explorer/gcc-4.8.5/include/c++/4.8.5/bits/stl_algobase.h:335:18:   required from 'static _OI std::__copy_move<false, false, std::random_access_iterator_tag>::__copy_m(_II, _II, _OI) [with _II = std::vector<double>*; _OI = std::ostream_iterator<std::vector<double> >]'

/opt/compiler-explorer/gcc-4.8.5/include/c++/4.8.5/bits/stl_algobase.h:390:70:   required from '_OI std::__copy_move_a(_II, _II, _OI) [with bool _IsMove = false; _II = std::vector<double>*; _OI = std::ostream_iterator<std::vector<double> >]'

/opt/compiler-explorer/gcc-4.8.5/include/c++/4.8.5/bits/stl_algobase.h:428:38:   required from '_OI std::__copy_move_a2(_II, _II, _OI) [with bool _IsMove = false; _II = __gnu_cxx::__normal_iterator<std::vector<double>*, std::vector<std::vector<double> > >; _OI = std::ostream_iterator<std::vector<double> >]'

/opt/compiler-explorer/gcc-4.8.5/include/c++/4.8.5/bits/stl_algobase.h:460:17:   required from '_OI std::copy(_II, _II, _OI) [with _II = __gnu_cxx::__normal_iterator<std::vector<double>*, std::vector<std::vector<double> > >; _OI = std::ostream_iterator<std::vector<double> >]'

<source>:17:96:   required from 'ostream& operator<<(ostream&, std::vector<std::vector<double> >&) [with ostream = std::basic_ostream<char>]'

<source>:26:18:   required from here

/opt/compiler-explorer/gcc-4.8.5/include/c++/4.8.5/bits/stream_iterator.h:198:13: error: cannot bind 'std::ostream_iterator<std::vector<double> >::ostream_type {aka std::basic_ostream<char>}' lvalue to 'std::basic_ostream<char>&&'

  *_M_stream << __value;

             ^

In file included from <source>:2:0:

/opt/compiler-explorer/gcc-4.8.5/include/c++/4.8.5/ostream:602:5: error:   initializing argument 1 of 'std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char; _Traits = std::char_traits<char>; _Tp = std::vector<double>]'

     operator<<(basic_ostream<_CharT, _Traits>&& __os, const _Tp& __x)

     ^

Compiler returned: 1
Justin
  • 24,288
  • 12
  • 92
  • 142
mystery_doctor
  • 404
  • 2
  • 11
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/173906/discussion-between-mystery-doctor-and-justin). – mystery_doctor Jun 27 '18 at 18:06

1 Answers1

0

The reason why std::ostream_iterator doesn't work with your code is because the operator<< you defined is in the global namespace, not in namespace std.

std::ostream_iterator is defined to call operator<< like ostr << value, which performs argument-dependent lookup (ADL). ADL doesn't find any operator<< in this case, since there is no operator<< between std::ostream& and std::vector<double> defined inside namespace `std.

After ADL finds associated namespaces and classes, the overload set is merged with those found by unqualified name lookup. To quote cppreference on unqualified name lookup:

[N]ame lookup examines the scopes as described below, until it finds at least one declaration of any kind, at which time the lookup stops and no further scopes are examined.

emphasis added

Since std::ostream_iterator is inside the std namespace, its call to ostr << value is inside the std namespace. There are other operator<<s defined inside the std namespace, so unqualified name lookup finds an operator<< and stops. This happens before the discovered function is considered for whether it could be compatible.


Do not add operators to a type you do not own, unless you own the other type. In order to work properly with the language, operators must be defined in the same namespace as the type (so that it can be found via ADL). However, adding functions to a namespace you do not own is not good practice, and it will cause problems. For the case of std, it's actually undefined behavior.

Justin
  • 24,288
  • 12
  • 92
  • 142