58

I was wondering how std::cout is able to use << as it does.

My main puzzlement is with whether std::cout as an instance of something. Basically, how is << defined? If I do this for a custom class, I need an instance of some sort...

I could see implementing it as kind of a hack with void pointers or something, but I'd like to see the actual way it's done.

Does anyone here know? Thanks

Eitan T
  • 32,660
  • 14
  • 72
  • 109
Ori
  • 4,961
  • 10
  • 40
  • 39
  • If you really want to see a whole lot about iostreams, you might want to take a look at a copy of http://www.amazon.com/Standard-IOStreams-Locales-Programmers-Reference/dp/0201183951 – Billy ONeal Apr 01 '11 at 03:36
  • 17
    The `cout <<` and `cin >>` expressions are the reasons why C++ is so unintuitive and hard to learn. – user541686 Apr 01 '11 at 03:38
  • 17
    @Mehrdad: If you're choosing a language because of how one writes to the console you're choosing the language for the wrong reasons. I agree the `iostream` library is complex, but there are plenty of awesome aspects to the language which aren't related to iostream at all (i.e. the STL). (If you don't like iostream, then don't use it. Use the bits in `` or bits specific to your platform.) – Billy ONeal Apr 01 '11 at 03:42
  • 10
    @Billy: It wasn't the console writing that I was talking about, it was the fact that a beginner needs to know **operator overloading** before he can even start to understand what's actually happening... – user541686 Apr 01 '11 at 03:51
  • 2
    @Billy: Furthermore, `a << b;` should *not* have been an allowed statement; it's just like saying `2 + 3;`, which -- even though valid -- makes no sense. If anything, they should've made it be `a <<= b;` so that it would at least give the *hint* to the reader that some kind of assignment is actually taking place. – user541686 Apr 01 '11 at 03:53
  • 2
    @Mehrdad: Saying something is bad because you don't understand it is not sufficient reasoning to prove it is bad. `2 << 3` doesn't invoke operator overloading at all. As for making it illegal, you can write nonsense in any language. The language's job is not to prevent you from doing stupid things. – Billy ONeal Apr 01 '11 at 03:55
  • 1
    Err.. you edited your comment and now mine above doesn't make as much sense. But the ending point is still true. Can you do bad things with operator overloading? Sure. But just because you can do bad things with a feature doesn't mean that feature shouldn't exist. – Billy ONeal Apr 01 '11 at 03:57
  • Streams feel incredibly unintuitive, especially when it comes to formatted IO and stream manipulators. @Billy: Funny you mention that (about using C-style IO), I asked a [question about doing just that](http://stackoverflow.com/questions/2880248/stdstring-resize-and-stdstring-length) and the response from C++ elitists were basically "That's wrong, you're not using C++ the way it's meant to be used". – dreamlax Apr 01 '11 at 03:59
  • 1
    @Billy: Operator overloading and the definition of a "statement" were **separate** issues I was referring to, both with their own valid uses. And just because you understand it doesn't mean it's good either... I certainly understand what's happening but I still think it's bad: just as the phrase `Two plus three ` makes no sense as a **sentence** in English, the code `2 + 3;` or `a << b;` doesn't make really make much sense as a **statement** either, and the fact that it's actually valid makes C++ really hard to learn (not to mention making it easier for you to shoot yourself in the foot). – user541686 Apr 01 '11 at 03:59
  • 2
    @dreamlax: Well, hardcore C++ guys like myself are going to want you to use iostreams of course, but there are plenty of people who don't like them. C++ is not a language which forces much upon you. If you don't like the library then don't use it. – Billy ONeal Apr 01 '11 at 04:00
  • 1
    @Mehrdad: It's not a language's job to make it hard to shoot oneself in the foot. It's the language's job to cleanly express one's thoughts in a form the computer can understand. In a language where one can address memory and cause segfaults and buffer overflows and all manner of other nasty things, making hard-to-shoot-in-footness would have been (and is) an insane design goal. If you want a language with such goals then get off our lawn and go use Java or C#. – Billy ONeal Apr 01 '11 at 04:02
  • 5
    @Billy: It's not the I/O stream library that's the issue. Given what C++ has, the library is pretty well-written, actually. The problem is that the *language* allows for pretty much *any* expression to be a statement -- something that many other languages don't allow, because it honestly **makes no sense** to say `2 + 3;`, and it just leads to the confusion of both the programmer and the reader. Had they actually forced the use of some **assignment** operator like `cout <<= "hi";` instead of `cout << "hi";`, IMHO the language (and consequently the I/O library) would've been a lot better. – user541686 Apr 01 '11 at 04:04
  • @Mehrdad: It also makes no sense to do `a.Select((x) => x)` (where `a` is an `IEnumerable`) in C# either, but the language lets you do it. If you want to be dumb you can be dumb anywhere. If you want "intuitive to a beginner" as a language requirement, then don't use C++. – Billy ONeal Apr 01 '11 at 04:08
  • @Billy: That's not a *language* issue... `Select` is not part of the C# language. – user541686 Apr 01 '11 at 04:09
  • @Mehradad: Why does it matter if it is a language issue? Unintuitive is unintuitive. – Billy ONeal Apr 01 '11 at 04:10
  • 1
    It's funny how many languages have borrowed C-style IO (Ruby, Python, Shell, Go, Java, C# [to an extent]), but I can't think of one that uses C++'s style. – dreamlax Apr 01 '11 at 04:12
  • @Billy: Yes, you're completely right -- it's just as bad to `a.Select((x) => x)` in C# as it is to write `a << b;` in C++, because neither really makes sense as a statement. Not sure what you're trying to say, though... it just supports my point all the more. :) – user541686 Apr 01 '11 at 04:12
  • @Mehrdad: My point is that saying "I can make feature x do nonsense!" is not justification for removal of that feature (at least in terms of general purpose programming languages). Anyone can write code in any language using most any feature which does nonsense. You can write nonsense anytime you wish. The language doesn't require that code be of high quality; that's the programmer's responsibility. – Billy ONeal Apr 01 '11 at 04:14
  • @dreamlax: Actually I agree with you here -- I'm not a fan of iostreams -- the main reason I prefer it over cstdio is that iostreams makes writing testable code easy; cstdio doesn't provide a way to make (for example) a `FILE *` point to a string. – Billy ONeal Apr 01 '11 at 04:15
  • @Billy: I'm *not* saying `I can make feature x do nonsense!`; I'm saying *Feature x **is** nonsense!*, where `x` is the ability to *statements* like `a << b;`. I'm *not* opposing operator overloading, nor am I saying *anything* about the I/O library. What I'm saying is, `a << b;` should be **syntactically invalid** as a statement for the same reason that `C++ needs.` is an invalid sentence in English: neither makes much sense. Had it been `a <<= b;`, it would've actually made sense, because `=` *implies* the side effect of an assignment, whereas `<<` doesn't. – user541686 Apr 01 '11 at 04:19
  • @Mehrdad: How then do you support `a << b << c`? – Billy ONeal Apr 01 '11 at 04:28
  • @Billy: I think you missed the point: `a << b << c` **should** be valid, but `a << b << c;` **shouldn't**. (Notice the `;`.) – user541686 Apr 01 '11 at 04:29
  • @Mehrdad: Okay, `a << b << c;` then. It's of the form `expression;`, which you can't remove because it's why (for example) `a++;` works. – Billy ONeal Apr 01 '11 at 04:32
  • @Billy: It's easy: you *don't*. (See C#, Java, D, and a whole slew of other languages that get by perfectly without it.) – user541686 Apr 01 '11 at 04:33
  • Can you be a bit more specific? I think we can both agree that `a << b` needs to be a possible expression. I think we can both agree that `expression;` can't go, as I've already illustrated. How exactly would you resolve that issue? – Billy ONeal Apr 01 '11 at 04:35
  • @Billy: Yes, we both agree that `a << b` needs to be a possible *expresssion*. No, we do *not* both agree that *any* expression should be a valid *statement*; particularly, `a << b;` should *not* be a valid statement, because the `<<` operator does *not* convey the fact that it is causing an *assignment*. And by the way, *C# does **not** allow that.* – user541686 Apr 01 '11 at 04:37
  • 1
    But it's *not* causing an assignment. It's calling a function. Which the user can overload to do whatever they want. – Billy ONeal Apr 01 '11 at 04:38
  • @Billy: Take a look at this C# error when you type in `1 << 2;`, or anything of the form `a << b`, for that matter: `error CS0201: Only assignment, call, increment, decrement, and new object expressions can be used as a statement` – user541686 Apr 01 '11 at 04:39
  • @Billy: If you were designing a new version of English, would you let the *phrase* `C++ is not very.` be a grammatically correct *sentence*? – user541686 Apr 01 '11 at 04:41
  • 1
    @Mehrdad: Okay, then we disagree. Go back to 1978 change Dennis Ritchie's mind, and then it can be changed. The language is not designed to make nonsense invalid because someone's an idiot. It's designed for someone who knows what they're doing to express ideas. C# and C and C++ have different design goals. C# has preventing nonsense as a goal. C and C++ do not. – Billy ONeal Apr 01 '11 at 04:45
  • I don't see how your English examples have anything to do with the C++ examples. – Billy ONeal Apr 01 '11 at 04:46
  • @Billy: The connection is, `a + b;` in C++ is like `A plus B.` in English. The latter doesn't make sense as a sentence, and so neither should the former as a statement... I think it follows very logically, given that humans are meant to be reading code and not computers. – user541686 Apr 01 '11 at 05:06
  • 1
    @Mehrdad: as Billy says, `a << b` isn't necessarily causing an assignment. When it comes to streams, the newbie you're so keen to protect should be thinking of this is "stream b through a", so the vaguely arrow-like and directional nature of the less-than character is helpful, and for `cout` they don't care about buffering: they just know the data's sent and gone, so assignment would be misleading ("hey, doesn't it get stuck in cout? can't I get it back?"). And other languages *do* adopt C++ style: e.g. Ruby: `$stdout << 99 << " red balloons"`... look familiar? – Tony Delroy Apr 01 '11 at 06:25
  • @Tony: Would you mind reading the **fourth bold paragraph** [here](http://msdn.microsoft.com/en-us/library/ms229032.aspx)? It says: ***Do not be clever** when defining operator overloads. Operator overloading is useful in cases where it is immediately obvious what the result of the operation will be. For example, it makes sense to be able to subtract one DateTime object from another DateTime object and get a TimeSpan object. However, it is **not** appropriate to use the logical union operator to union two database queries, **or to use the shift operator to write to a stream**.* (emphasis mine) – user541686 Apr 01 '11 at 07:15
  • @Tony: ... and please don't denounce it as C#-specific. It applies to many more languages than just C#, and the *reason behind the "do not be clever"* -- namely **human readability** -- applies *everywhere*. There's no reason to make things more complicated than they need to be; that's where C++ failed, with things like *misuse* of operator overloading and the poor choice of allowing *any* expression whatsoever to be counted as a statement. – user541686 Apr 01 '11 at 07:18
  • @Mehrdad: MSDN's cheapshot is exactly that. "C++ failed"? I'm sorry, but I don't accept your self-appointment as judge of that. Readability is important, and given the hands-on teaching experience Stroustrup has had I'm sure he'd have done something about << if new developers had trouble with it. You're just being melodramatic, but do feel free to use C++0x variadic macros to implement a type-safe "print(a, b, c)" if you miss BASIC. ;-P – Tony Delroy Apr 01 '11 at 09:51
  • @Tony: So you think MSDN just said that to promote C# against? If so, how is it a promotion -- is it because (God forbid!) it's good advice? If not, then why did it say that? Just to mislead people? Also, *you're **again** missing the fact that I'm not referring to I/O*! I'm referring to how expressions and statements are handled in general... I'm not sure why you consistently mention the I/O problem, because I'm in no way singling out iostream against any other part of C++. – user541686 Apr 01 '11 at 10:17
  • 1
    @Tony: Try taking a look at [this page](http://www.parashift.com/c++-faq-lite/operator-overloading.html#faq-13.9), having to do with C++ itself: *If you provide constructive operators, they **should not change their operands**. For example, **`x + y` should not change `x`**.* (Ditto with `<<`.) – user541686 Apr 01 '11 at 10:19
  • @Tony: Eh? `Given the hands-on teaching experience Stroustrup has had I'm sure he'd have done something about << if new developers had trouble with it.` So you think beginners have no trouble with it? For one thing, *I* had trouble with it when I was a beginner. For another thing, I'm not really alone; my friends ask me the same question when they're learning C++, and even others ask [this same question](http://stackoverflow.com/questions/5123674/and-in-c) on SO. I doubt that Stroustrup had the eye of a beginner when he designed C++, and I'm failing to see why you called this a cheapshot. – user541686 Apr 01 '11 at 10:43

1 Answers1

56

std::cout is an instance of std::ostream. std::cout << "something" calls one of the operator<< overloads as would be done for any instance of std::ostream.

It's "special" in that it references the console, but otherwise it behaves exactly as an ofstream or an ostringstream would.

EDIT: Chaining works just like it works for any other operators. Consider:

class MyType
{
    friend std::ostream& operator<<(std::ostream& target, const MyType& source);
    int val;
public:
    MyType()
        : val(0)
    { }
    MyType& Add(int toAdd)
    {
        val += toAdd;
        return *this;
    }
};

MyType& operator+(MyType& target, int toAdd)
{
    return target.Add(toAdd);
}

std::ostream& operator<<(std::ostream& target, const MyType& source)
{
    target << source.val;
    return target; //Make chaining work
}

int main()
{
    MyType value1;
    value1 + 2 + 3 + 4;
    std::cout << value1 << " and done!" << std::endl;
}

In this case, the chaining for +s on MyType work for the same reason the <<s work on top of std::ostream. Both + and << are left-associative, which means they're evaluated from left to right. In the case of overloaded operators, the operator is replaced with the equivalent function call.

EDIT2: In a bit more detail:

Let's say you're the compiler and you're parsing

std::cout << value1 << " and done!" << std::endl;

First, << is left associative, so you start on the left. You evaluate the first << and turn it into the function call:

operator<<(std::cout, value1) << " and done!" << std::endl;

You then see that you once again have a std::ostream (the result of the call to operator<<), and a char *, which you once again turn into the function call:

operator<<(operator<<(std::cout, value1)," and done!") << std::endl;

and so on until you've processed the entire statement.

xdevs23
  • 3,824
  • 3
  • 20
  • 33
Billy ONeal
  • 104,103
  • 58
  • 317
  • 552
  • 2
    +1 But (to play an advocate), how does chaining work? (e.g. `std::cout << a << b ...`) –  Apr 01 '11 at 03:37
  • Does it also return an std::ostream? I mean you can also do cout << "something" << "something else". How does the second operator<< operate? – highBandWidth Apr 01 '11 at 03:38
  • 6
    @pst: All of the `operator<<` overloads are of the form `std::ostream& operator<<(std::ostream& target, TYPE& source)`, and they return the reference passed in as the source. That is, `std::cout << a << b;` is the same as `operator<<(operator<<(std::cout, a), b);` – Billy ONeal Apr 01 '11 at 03:40
  • http://www.cplusplus.com/reference/iostream/ostream/operator%3C%3C/ provides some details of what's going on. – geekosaur Apr 01 '11 at 03:47
  • @pst + @highBandWidth: Added some more to the answer to explain a bit more clearly (hopefully) – Billy ONeal Apr 01 '11 at 03:52
  • 2
    `cout` doesn't reference the console, it refers to the operating system's default output. In some operating systems, this happens to be a console. – Mooing Duck Jul 25 '13 at 19:48
  • 1
    @MooingDuck: That's true. But I don't think going into that discussion makes sense for an answer like this aimed primarily at beginners. – Billy ONeal Jul 25 '13 at 21:56
  • C++ Primer by Prata details how and why. I read an older edition of it, though, with C++ 03 I think, or near that time. – Stephen J Dec 09 '13 at 07:46
  • For those who still were confused about the chaining (like I was): As we know, operations are basically nothing more than methods of classes. This means that they have a return value, which they sort of can be "replaced with" (for instance, writing `3 + 5` can be replaced with `8`). In the `cout`and `cin` object's case, these return-values are always references to `*this` (in which case, the reference refers to the same `cout`/`cin`-object as you performed the operation on). So when you write: `cout << 'a' << 'b'`, the first statement (`cout << 'a'`) returns "`cout`" which leaves `cout << 'b'` – Max Apr 21 '14 at 09:00
  • Notice: The `3 + 5`-example isn't of the same concept (doesn't conclude objects), but you still get the point... – Max Apr 21 '14 at 09:12
  • Is the << inside operator << the original << operator of it's a recursion? – Guerlando OCs Oct 22 '18 at 14:21
  • @MooingDuck it would basically get a handle to stdout on windows, which is whatever the process has set. `SetStdHandle` sets the handles for stdin/out/err in a structure `ProcessParameters` pointed to by the PEB. After getting the handle it will use it in a `WriteFile()` call, which will write to the screen buffer that the process has set (both setting a screen buffer and `WriteFile` to a standard stream appear to result in a IPC call to CSRSS) – Lewis Kelsey Apr 07 '20 at 01:57
  • @GuerlandoOCs: No. One is `operator<<(ostream, MyType)`, and it calls `operator<<(ostream, int)`. They happen to have the same name, but they're different overloads, so it's not recursion. – Mooing Duck Apr 08 '20 at 17:00