0

This is related to the difference-between-cout-x-and-cout-operator-x question, but still a little different...

#include <iostream>

int main(){
    std::cout << "hello" << std::endl;

    std::cout.operator<<("hello2");
    std::cout.operator<<(std::endl);

    operator<<(std::cout, "hello3");
//    operator<<(std::cout, std::endl);

    return 0;
}

Q1: Why does std::cout.operator<<("hello2"); work?

From other answers on SO I would expect the compiler to complain since operator<< is meant to be a free function and not a member of cout. On my system, however, it prints "0x10d54df31". And, stranger yet, the following line correctly correctly executes std::endl.

Q2: Why does operator<<(std::cout, std::endl); not work?

I know that std::endl is a function, but it seems strange (to me) that the hello3 output works whilst the `std::endl' doesn't. Instead the compiler throws an error:

main.cpp:10:4: error: no matching function for call to 'operator<<'
    operator<<(std::cout, std::endl);

Q3: How can the first std::cout << "hello1" << std::endl; be written in operator<<(...) form?

If the first two questions have been answered, then this has probably already covered. It's the point of this learning exercise, so seems sensible to ask it explicitly.

Community
  • 1
  • 1
GnomeDePlume
  • 1,642
  • 2
  • 15
  • 31
  • This sounds very much like **homework**, questions that couldn't very well be posed without knowing the answers. – Cheers and hth. - Alf Jan 02 '14 at 17:13
  • @Alf definitely not part of my homework! Glad to hear that my questions sound well informed, though :) – GnomeDePlume Jan 02 '14 at 17:16
  • @Jerry could you tell me what editing changes you made, please? I'll try improve the format of future questions... – GnomeDePlume Jan 02 '14 at 18:16
  • 1
    @GnomeDePlume: If you click on the "edited N mins ago" it'll show you a diff. In this case it was mostly just some mis-matched quotes. Looking at it again, however, my last edit (changing "is" to "has") was clearly a mistake. I think I intended to change it to "was". – Jerry Coffin Jan 02 '14 at 18:18

4 Answers4

3

Operators can be implemented in different ways, in particular an operator<< for which the left hand side is your type can be implemented as either a free function or as a member function of that left hand side type.

While you must implement ostream& operator<<(ostream&, MyType const&) as a free function (since MyType is not the left hand side), the library implementation can choose* to implement operator<< for some fundamental types insde the std::ostream type (which is really a particular instantiation of a template, I am trying to ignore the details).

Edit: After checking with the standard this is incorrect:

This is what you are noticing in the code, the overload that takes a const char* is implemented as a member of ostream (basic_ostream<char,char_traits<char>).

The overloads taking manipulators are implemented as member functions (Q2), and there is an implicit conversion from const char* to const void* that will be picked if you use the syntax for explicitly calling a member operator (Q1). For Q3, the answer would be:

operator<<(std::cout, "Hello").operator<<(std::endl);

* The implementation is actually not free to choose, since the standard mandates the signatures.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • Let's see if I understand this... The use of `operator<<` with `std::endl` (a manipulator) is implemented as a member function of `cout` (or other similar object). So that line works. – GnomeDePlume Jan 02 '14 at 17:34
  • However, the use of `operator<<` with a fundamental type is not (necessarily) implemented as a member function... – GnomeDePlume Jan 02 '14 at 17:35
  • 1
    Correct, you can take a look at the declaration of `basic_ostream` in any of the C++ standard drafts to get the list (27.7.3.1) – David Rodríguez - dribeas Jan 02 '14 at 17:39
  • I still don't understand why `operator<<` must always be used as a free function if used with a user-defined type (`MyType`), though... If the free function can handle arbitrary user types, why not the member functions of `std::ostream` (such as `cout`)? – GnomeDePlume Jan 02 '14 at 17:39
  • @GnomeDePlume: operators can be implemented as member functions *only* on the class that is the left-hand-side, not the right hand side. To be able to implement `operator<<` for `MyType` as a member in `std::basic_ostream` you would need to modify the definition of `std::basic_ostream`, but that is provided by the implementation and cannot be modified. – David Rodríguez - dribeas Jan 02 '14 at 17:44
  • Got'ya. Okay, possibly the last sub-question: the conversion from `const char*` to `const void*` is done to convert the argument (in this case `"hello2"` into a form that `std::cout.operator` can handle. So it takes my *pointer-to-a-character* and turns it into a *pointer-to-a-function-returning-void*. Is the output that it prints the function address for that (non-existent) void function? – GnomeDePlume Jan 02 '14 at 17:53
  • 1
    no, there's no function thing involved. it's just a pointer to `void`. because that's the formal argument type. – Cheers and hth. - Alf Jan 02 '14 at 17:59
  • 1
    @GnomeDePlume: There is no pointer to function, just pointer to `void`, that is an *address*, which in this case will be the address of the first character in the C-style string. – David Rodríguez - dribeas Jan 02 '14 at 18:04
  • Okay. [void represents the absence of type](http://www.cplusplus.com/doc/tutorial/pointers/#void_pointers) (I'm learning about all *types* of things today. It makes sense that the address is output, since the compiler cannot know what data type is stored in memory at that position... – GnomeDePlume Jan 02 '14 at 18:11
1

Some overloads of operator<< are class members, others are not.

In C++03 this created some baffling call scenarios since a reference to non-const (argument of the not-member) can't be bound to an rvalue, but in C++11 at least one such has been fixed by introducing an rvalue reference argument overload.

So, which calls compile or not depends more in general also on the C++ standards version, C++03 or C++11.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • I tried compiling (g++) with with '-std=c++11' but it made no difference to the calls in the test program. Useful to know, though! – GnomeDePlume Jan 02 '14 at 17:28
  • 1
    @GnomeDePlume: at the moment Codepad is one pastebin that uses a pretty old compiler. so you can look at the example at (http://codepad.org/h7WXNhuW). unfortunately that code compiles without error when I specify `-std=c++03` to MinGW g++ 4.7.2. – Cheers and hth. - Alf Jan 02 '14 at 17:49
1

There is a bunch of member output operators defined in std::ostream. In retrospect that was probably an error but when IOStreams were first created I think it was actually necessary. These member operators include the overloads taking function pointers which means you'll need to use member notation for those. The operators using C-strings are non-member overloads, i.e., you need to use the non-member function call notation to get the C-string overload. When you call the member operator with a char const* the char const* will be converted to void const* for which there is a member output operator.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • That conversion from `char const*` to `void const*` sounds nasty. Is there a reason for it, or is just another casualty of [explicit](http://stackoverflow.com/questions/121162/what-does-the-explicit-keyword-in-c-mean) not being the default? – GnomeDePlume Jan 02 '14 at 17:43
  • 2
    @GnomeDePlume: Well, the best match for `out.operator<< (str)` for an `std::ostream& out` and a `char const* str` is the overload `std::ostream::operator<< (void const*)` and this is, thus, chosen. When using operator call notation `std::operator<< (std::ostream&, char const*)` is a better match. – Dietmar Kühl Jan 02 '14 at 17:54
1

Your questions can be broken down to member functions an non-member functions.

Having 13.5.2 Binary operators

A binary operator shall be implemented either by a non-static member function (9.3) with one parameter or by a non-member function with two parameters. Thus, for any binary operator @, x@y can be interpreted as either x.operator@(y) or operator@(x,y). If both forms of the operator function have been declared, the rules in 13.3.1.2 determine which, if any, interpretation is used.

Omitting a quote of 13.3.1.2 the member function (operator) is preferred.

The line 'std::cout << "hello" << std::endl;' involves non member functions. Each 'std::cout.operator' is an explicit member function call.

  • Q1 is the member operator<<(const void*)
  • Q2 there is no member taking a function
  • Q3 It is not possible