【问题标题】:std::ostream recognize without defining headerstd::ostream 无需定义标头即可识别
【发布时间】:2019-01-25 12:27:14
【问题描述】:

我创建了这段代码:

main.cpp

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

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

    getchar();
    return 0;
}

报价.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;
};

如果我没有在 Main.cpp 中包含 iostream 头文件,如预期的那样,std::cout 将无法识别。我的问题是:如果 iostream 不包括在内,为什么在 Quote.h 中使用 std::ostream 没有问题? coutostream 都在上述库中定义,为什么 cout 的使用是一个问题而 ostream 不是?

我正在使用 VS 2017,以防此信息很重要。

【问题讨论】:

  • 没有任何规则可以阻止其中一个标准标头包含另一个标头,因此有时会根据实施情况发生这种情况。
  • &lt;string&gt; 可能包括&lt;iostream&gt;&lt;iosfwd&gt;,反之亦然。 - 但你永远不应该依赖这个;始终明确包含您需要的所有标题。
  • @NeilButterworth:我希望它包含&lt;iostream&gt;:它不需要特定于该标题的任何内容!流定义可以从&lt;istream&gt;&lt;ostream&gt;获取。
  • @Dietmar 好吧,我当然也希望如此。但它可以。

标签: c++ visual-studio header-files iostream


【解决方案1】:

所有现有答案都集中在#include &lt;string&gt;。我想指出另一边。考虑稍微修改的版本:

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{};
}

如您所见,#include &lt;string&gt; 已被注释掉,quote.h 仍不包括 iostream,程序仍可编译。这样做是因为只有源文件(.cpp 或翻译单元)被直接编译。标题实际上包括在内。现在,如果我们在main.cpp 中真正包含quote.h,我们会得到:

#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)

这是实际编译的内容。注意这里一切正常,#include &lt;iostream&gt;std::ostream 使用之前。

正如在对another answer 的评论中正确指出的那样,这就是为什么始终维护包含它们所依赖的所有标头的自给自足标头很重要的一个示例。

正如@Evgeny 在评论中指出的,请查看recommendations about organising our includes

【讨论】:

  • 请在您的回答中提及如果包含的顺序发生更改会发生什么:#include &lt;iostream&gt; // #include "Quote.h"#include "Quote.h" // #include &lt;iostream&gt;。这就是为什么 STL 标头应该位于 #include 列表末尾的原因。 stackoverflow.com/questions/2762568/…
【解决方案2】:

标头&lt;string&gt; 使用std::ostream 声明了一个输出运算符。您正在使用的实现似乎使std::ostream 普遍可用。

C++ 标准定义了哪些标头使哪些声明至少可用。它不禁止提供其他名称。不同的实现可能会选择不使声明可用。我曾尝试在我的实现中只提供严格的强制声明,但事实证明这并不像听起来那么简单。

【讨论】:

    【解决方案3】:

    您在头文件中包含&lt;string&gt;。如果您转到 string 标头,您将看到第一行(在 VS2017 中):

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

    然后转到istream 标头:

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

    我认为这已经回答了您的问题。但是,这取决于实现,您不应依赖此,而应明确包含 iostream 标头。

    【讨论】:

      【解决方案4】:

      标头&lt;string&gt;提供the extraction and insertion operators for std::string,因此它必须确保std::ostream至少是前向声明的;您的代码仅使用对 ostream 的引用,前向声明就足够了,加上前面提到的插入运算符,它已正确声明。

      因此,严格来说,您的标头所需的所有内容都已由标头&lt;string&gt; 提供,尽管为了清楚起见,我可能会明确包含&lt;iosfwd&gt;

      【讨论】:

        【解决方案5】:

        如果不包含&lt;iostream&gt;,为什么在Quote.h 中使用std::ostream 没有问题?

        &lt;iostream&gt; 间接获得#included。

        最好不要依赖这种间接#includes。您不能指望它在所有平台上都是正确的。它甚至可能从调试版本更改为发布版本。

        当您想使用类或函数时,最好直接在文件中查找应该提供类定义和函数声明的标头的标准,以及 #include 标头。

        【讨论】:

        • 即使我们删除#include &lt;string&gt;,程序仍然可以正常编译。请看我的回答。这是完整答案的重要组成部分,我说得对吗?
        • @AndriyTylychko,您的回答提供了另一个重要原因,为什么不在另一个头文件中#includeing 所需的头文件可能是一个隐藏问题。理想情况下,头文件应该是自给自足的。如果不能编译只有一行#include "quote.h"的.cc文件,那么头文件是不自给的。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-21
        • 2018-01-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多