3

I created this code:

Main.cpp

#include <iostream>
#include "Quote.h"

int main()
{
    derived().print(std::cout);

    getchar();
    return 0;
}

Quote.h

#pragma once
#include <string>

class base {
public:
    std::string name() { return basename; }
    virtual void print(std::ostream &os) { os << basename; }

private:
    std::string basename = "abc";
};

class derived : public base {
public:
    void print(std::ostream &os) { base::print(os); os << " " << i; }

private:
    int i = 0;
};

If I don't include the iostream header file in Main.cpp, as expected, std::cout is not recognized. My question is: Why there's no problem with the use of std::ostream in Quote.h if iostream is not included?. Either cout as ostream are defined in the aforementioned library, why the cout use is a problem and ostream not?

I'm using VS 2017, in case this info is important.

  • There is no rule that prevents one of the standard headers from including another so sometimes this will happen depending on implementation. – drescherjm Aug 18 '18 at 21:33
  • 2
    `` probably includes `` or ``, or vice versa. - but you should never depend on this; always explicitly include all the headers who's definitions you need. –  Aug 18 '18 at 21:35
  • @NeilButterworth: I’d hope it does _not_ include ``: it doesn’t need anything specific to that header! The stream definitions can be obtained from `` and ``. – Dietmar Kühl Aug 18 '18 at 22:10
  • @Dietmar Well, of course I hope so too. But it can. –  Aug 18 '18 at 22:16

5 Answers5

3

All existing answers concentrate on #include <string>. I'd like to point at another side. Consider slightly modified version:

quote.h:

#pragma once

// uncomment to get an analog of original example
// #include <string>

struct Quote {};

std::ostream& operator<<(std::ostream& os, Quote const&)
{
    return os << "quote\n";
}

main.cpp:

#include <iostream>
#include "quote.h"

int main()
{
    std::cout << Quote{};
}

As you see #include <string> is commented out, quote.h still doesn't include iostream and the program still compiles. It does because only source files (.cpp, or translation units) are directly compiled. Headers are literally included. Now if we literally include quote.h in main.cpp we get:

#include <iostream>

// uncomment to get an analog of original example
// #include <string>

struct Quote {};

std::ostream& operator<<(std::ostream& os, Quote const&)
{
    return os << "quote\n";
}

int main()
{
    std::cout << Quote{};
}

(online)

It's what actually gets compiled. Notice everything is alright here, #include <iostream> before std::ostream usage.

And as was correctly pointed in comment to another answer, this is an example why it's important to always maintain self-sufficient headers that include all headers they depend on.

As @Evgeny pointed out in the comment, please check recommendations about organising our includes.

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
Andriy Tylychko
  • 15,967
  • 6
  • 64
  • 112
  • 1
    Please mention in your answer what would happen if the order of includes is changed: `#include // #include "Quote.h"` to `#include "Quote.h" // #include `. This is the reason why STL headers should be at the end of `#include` list. https://stackoverflow.com/questions/2762568/c-c-include-file-order-best-practices – Evg Aug 19 '18 at 07:05
2

The header <string> declares an output operator using std::ostream. It seems the implementation you are using does so in a way making std::ostream generally available.

The C++ standard defines which headers make which declarations available at least. It doesn’t prohibit against additional names being made available. Different implementations may choose to not make the declarations available. I have tried making strictly only mandated declarations available in my implementation but this turns out to be not quite as simple as it sounds.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
2

You include <string> in your header file. If you go to the string header you will see the first lines as (in VS2017):

// string standard header
#pragma once
#ifndef _STRING_
#define _STRING_
#ifndef RC_INVOKED
#include <istream> <----- here
#include <xstring_insert.h>

and going to the istream header:

// istream standard header
#pragma once
#ifndef _ISTREAM_
#define _ISTREAM_
#ifndef RC_INVOKED
#include <ostream> <-- here

which I think already answers your question. However, this is implementation dependent and you should not rely on this but include explicitly the iostream header.

FrankS101
  • 2,112
  • 6
  • 26
  • 40
1

The header <string> provides the extraction and insertion operators for std::string, so it has to make sure that std::ostream is at least forward-declared; your code only uses a reference to ostream, for which a forward declaration suffices, plus the aforementioned insertion operator, which is correctly declared.

So, strictly speaking, all that is needed by your header is already provided by the header <string>, although I'd probably explicitly include <iosfwd> for clarity.

Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
1

Why there's no problem with the use of std::ostream in Quote.h if <iostream> is not included?

<iostream> get #included indirectly.

It's best not to rely on such indirection #includes. You can't count on it being true on all platforms. It may even change from debug build to release build.

When you want to use a class or a function, it's best to lookup the standard for the header that's supposed to provide the definition of the class and the declaration of the function, and #include the header directly in your file.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • Even if we remove `#include ` the program still compiles fine. Please see my answer. Am I right that it's an important part of a complete answer? – Andriy Tylychko Aug 18 '18 at 22:18
  • 1
    @AndriyTylychko, your answer provides another important reason why not `#include`ing a required header file in another header file can be a hidden problem. Ideally, header files should be self-sufficient. If you can't compile a .cc file that has one line, `#include "quote.h"`, then the header file is not self-sufficient. – R Sahu Aug 18 '18 at 22:24