6

I am trying to pretty print an STL container. What I am trying to is to print elemets of a container separated with a delimiter. However I have came across a couple of problems.

1. g++ vs VC++

ostream& operator<<(ostream& o, const vector<string>& v) {
    copy(v.begin(), v.end(), std::ostream_iterator<string>(o,","));
}

int main()
{

    vector<string> s_v;
    s_v.push_back("one");
    s_v.push_back("two");

    cout << s_v;

}

g++ (gcc version 4.4.0 on mingw32) can compile it an works fine. VC++ (Visual Studio 9) can not compile this code.

error C2679: binary '<<' : no operator found which takes a right-hand operand of type 'const std::string' (or there is no acceptable conversion)
1>        c:\program files (x86)\microsoft visual studio 9.0\vc\include\ostream(653): could be 'std::basic_ostream<_Elem,_Traits> &std::operator <<<char,std::char_traits<char>>(std::basic_ostream<_Elem,_Traits> &,const char *)'
1>        with
1>        [

why is this? Is this code illegal? or it is just VC++ beign VC++?


2. Unused template variable breaks compilation.

if now I add a template to the ostream like this (it is not used, just sitting there)

template <typename T>  // <----- Here
ostream& operator<<(ostream& o, const vector<string>& v) {
    copy(v.begin(), v.end(), std::ostream_iterator<string>(o,","));
}

int main()
{

    vector<string> s_v;
    s_v.push_back("one");
    s_v.push_back("two");

    cout << s_v;

}

gcc can not match the operator anymore.

    error: no match for 'operator<<' in 'std::cout << s_v'

and a lot more candidates...

Why? the template is unused. Should it matter?


EDIT: This is solved. I had to return o;

3. Used template

template <typename T>
ostream& operator<<(ostream& o, const vector<T>& v) {
    copy(v.begin(), v.end(), std::ostream_iterator<T>(o,","));

    return o; // Edited
}

int main()
{

    vector<string> s_v;
    s_v.push_back("one");
    s_v.push_back("two");

    vector<int> i_v;
    i_v.push_back(1);
    i_v.push_back(2);

    cout << s_v;
    cout << i_v;
}

If I know use the template type. g++ can compile it but then terminates with an exception.

terminate called after throwing an instance of 'std::bad_cast'
  what():  std::bad_cast

VC++ is just sitting and watching gcc do all of this. Doesn't compile any of them.

Can someone please clarify this things for me? Thank you.

12
  • Define your operator in std namespace. You'll see the difference
    – PiotrNycz
    Commented Apr 2, 2013 at 17:46
  • If the compiler can't infer the type of a template argument, you have to provide it yourself. So if you have template<typename T> void Foo() { ... }, you need to use Foo<Type>(). Obviously, you can't do that when you overload an operator. When you do use T, the compiler is able to infer its type, and this is why it works.
    – zneak
    Commented Apr 2, 2013 at 17:48
  • Also, your last example runs fine on ideone. You might want to add return o; to your operator<< though. This would most likely crash your program if you did cout << s_v << i_v.
    – zneak
    Commented Apr 2, 2013 at 17:54
  • 1
    @PiotrNycz Don't ever define an operator in std:: unless it involves a type you've defined somewhere. Otherwise, the code is illegal. Commented Apr 2, 2013 at 17:54
  • 1
    @Curious I can't reproduce the error. Once I've fixed the obvious errors (missing includes, missing std::, missing return), it works with my system (VC++ 11). Commented Apr 2, 2013 at 17:56

2 Answers 2

5

PREMISE:

First of all, the code is illegal because it misses a return statement (which is likely what is causing the exception that gets raised in the third version):

ostream& operator<<(ostream& o, const vector<string>& v) {
    copy(v.begin(), v.end(), std::ostream_iterator<string>(o,","));
    return o; // <== THIS ONE WAS MISSING
}

This injects undefined behavior to your program. Per Paragraph 6.6.3/1 of the C++11 Standard, in fact:

[...] Flowing off the end of a function is equivalent to a return with no value; this results in undefined behavior in a value-returning function.

Concerning your first question:

Once that is fixed, your code is fine, and the implementation of the Standard Library that is shipped with VC9 probably has a bug.

In fact, the compiler should look up for eligible overloads of operator << in the namespace of the arguments (std) and in the namespace where the call is being made (the global namespace). As long as your operator is defined in the global namespace and the statement cout << s_v is in the global namespace, overload resolution should succesfully pick your overload.

Concerning your second question:

Why? the template is unused. Should it matter?

That's simple: the compiler has no way of deducing T from the function arguments, so unless you specify it explicitly, this will result in a compilation error. However, specifying the template argument explicitly would mean do something like the following, which is close to non-sense:

::operator << <void>(std::cout, s_v);

In C++11 you could specify a default argument for T, which would make the function call legal, bug then again, for what purpose?

Concerning your third question:

When T is used in the type of at least one of the function parameters in a deduced context, the compiler will allow to deduce it from the function arguments (in this case, it will deduce T = std::string, and you don't have to specify it explicitly.

CONCLUSION:

So to sum it up: after adding the necessary return statement, the first and third versions of your program are legal and make sense, while the second is not and does not.

11
  • Concerning the code he actually posted for the first question: it won't compile with any compiler, because of missing includes. I've seen strange overload resolution with VC++ when includes are missing: including <iostream> might provide some (but not all) of <string>, so some things work, but others don't. Commented Apr 2, 2013 at 17:59
  • @JamesKanze: Right, I assumed the OP imported the correct headers and all the necessary ones, but it's true this can't be told for sure since the preliminary part is missing from the question's text
    – Andy Prowl
    Commented Apr 2, 2013 at 18:02
  • I am using std; and I have the proper includes.
    – user2237197
    Commented Apr 2, 2013 at 18:04
  • The problem was the return, and then the second part as explained by both of you.
    – user2237197
    Commented Apr 2, 2013 at 18:05
  • @Curious: This is not relevant to your problem, but having a using namespace std; is considered a bad programming practice, because it can easily lead to name clashes. You should replace that with using declarations (e.g. using std::cout) when practical, or if you really have to add a using namespace std;, then make sure it is in a function scope and not at global scope
    – Andy Prowl
    Commented Apr 2, 2013 at 18:05
1
  1. As posted, your code shouldn't compile with any compiler. You're missing includes, and a lot of std::. What I suspect is happening is that you don't have all of the necessary includes; in particular, that #include <string> is missing, and that g++ picks it up indirectly. Which is curious. The problem is usually the inverse: that VC++ picks up a lot of extra includes. At times, however, only partially (so you might end up knowing about std::string, but not about the non-member functions associated with it, like operator<<). Without seeing your actual includes, however, it's difficult to say.

  2. The compiler can only perform overload resolution on functions, not on function templates. Before starting overload resolution, it tries to instantiate an function templates with the correct name. Successful instantiation results in a function, which it adds to the overload set. But how is it supposed to instantiate your function template. It has no way of knowing what to use for T. So it doesn't instantiate (template argument deduction fails), and no instance of it finds its way into the overload set.

  3. I don't see anything immediate here. After adding the missing return in the operator<<, it compiles and runs correctly on VC++.

1
  • I am using std; and I have the proper includes. However it is odd that it works with VC for you. It still doesn't for me. The third proper version.
    – user2237197
    Commented Apr 2, 2013 at 18:06