3
votes

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.

5
There is no rule that prevents one of the standard headers from including another so sometimes this will happen depending on implementation. - drescherjm
<string> probably includes <iostream> or <iosfwd>, or vice versa. - but you should never depend on this; always explicitly include all the headers who's definitions you need. - user2100815
@NeilButterworth: I’d hope it does not include <iostream>: it doesn’t need anything specific to that header! The stream definitions can be obtained from <istream> and <ostream>. - Dietmar Kühl
@Dietmar Well, of course I hope so too. But it can. - user2100815

5 Answers

2
votes

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.

2
votes

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.

2
votes

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.

1
votes

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.

1
votes

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.