Explicit direct #include vs. Non-contractual transitive #include

Phi*_*ZXX 20 c++ header include c++17

Say we have this header file:

MyClass.hpp

#pragma once
#include <vector>

class MyClass
{
public:
    MyClass(double);

    /* ... */

private:
    std::vector<double> internal_values;
};
Run Code Online (Sandbox Code Playgroud)

Now, whenever we use #include "MyClass.hpp" in some other hpp or cpp file, we effectively also #include <vector>, despite the fact that we do not need it. The reason I am saying it is not needed is that std::vector is only used internally in MyClass, but it is not required at all to actually interact with this class.

As a result, I could write

Version 1: SomeOtherHeader.hpp

#pragma once
#include "MyClass.hpp"

void func(const MyClass&, const std::vector<double>&);
Run Code Online (Sandbox Code Playgroud)

whereas I probably should write

Version 2: SomeOtherHeader.hpp

#pragma once
#include "MyClass.hpp"
#include <vector>

void func(const MyClass&, const std::vector<double>&);
Run Code Online (Sandbox Code Playgroud)

to prevent a dependency on the internal workings of MyClass. Or should I?

I obviously realise that MyClass needs <vector> to work. So this may be more of a philosophical question. But would it not be good to be able to decide which headers get exposed when importing (i.e. limit what gets loaded into the namespace)? So that each header is required to #include what it needs, without getting away by implicitly including something that another header needed down the chain?

Maybe people can also shed some light on the upcoming C++20 modules which I believe address some aspects of this issue.

eer*_*ika 16

以防止依赖MyClass的内部工作原理。还是我应该?

是的,出于这个原因,您应该这样做。除非要指定MyClass.hpp保证包含<vector>,否则不能依赖其中的一个。并且没有充分的理由被迫提供这种保证。如果没有这样的保证,则您依赖MyClass.hpp的实现细节,该细节将来可能会更改,这将破坏您的代码。

我显然意识到MyClass需要向量才能起作用。

可以?它不能使用示例boost::container::small_vector吗?

在此示例中,MyClass需要std :: vector

但是,将来MyClass的需求如何?程序不断发展,今天一堂课的需求与明天一堂课的需求并不总是相同的。

但是,能够决定在导入时公开哪些标头不是很好

防止传递包含是不可能的。

C ++ 20中引入的模块是一项功能,可以代替pp-inclusion使用,旨在帮助解决此问题。

现在,您可以通过使用PIMPL模式(“实现的指针”)避免包括任何实现细节依赖项。但是PIMPL引入了一个间接层,更重要的是,它需要动态分配,这会影响性能。根据上下文,这些影响可能是微不足道的,也可能是重大的。

  • @ Phil-ZXX *'在此示例中,MyClass需要std :: vector'* –但这可以随时更改,不是吗? (2认同)

小智 7

您应该使用explicit #includes具有非破坏性的工作流程。假设MyClass在50个不同的源文件中使用了该文件。它们不包括在内vector。突然之间,你必须改变std::vectorMyClass.h其他一些容器。然后,所有50个源文件都将需要包含,vector或者您需要将其保留在中MyClass.h。这将是多余的,并且可能不必要地增加应用程序大小编译时间甚至运行时间(静态变量初始化)。

  • @Aconcagua一些标头的事情。特别是,具有由“漂亮计数器”保护的全局变量的标头。尝试在不使用任何内容的情况下包含`&lt;iostream&gt;`,然后查看大小的增加(假设其他任何东西都未使用`&lt;iostream&gt;`)。不过,这并不是避免使用全局变量的唯一原因,因此这很少见。 (3认同)

for*_*818 5

Consider that code is not just to be written once but it evolves over time.

Lets assume you wrote the code and now my task would be to refactor it. For some reason I want to replace MyClass with YourClass and lets say they have the same interface. I would simply have to replace any occurence of MyClass with YourClass to arrive at this:

/* Version 1: SomeOtherHeader.hpp */

#pragma once
#include "YourClass.hpp"

void func(const YourClass& a, const std::vector<double>& b);
Run Code Online (Sandbox Code Playgroud)

I did everything correct, but still the code would fail to compile (because YourClass is not including std::vector). In this particular example I would get a clear error message and the fix would be obvious. However, things can get messy rather fast if such dependencies span across several headers, if there are many of such dependencies and if the SomeOtherHeader.hpp contains more than just a single declaration.

There are more things that can go wrong. Eg the author of MyClass could decided that they actually can drop the include in favor of a forward declaration. Also then SomeOtherHeader will break. It boils down to: If you do not include vector in SomeOtherHeader then there is a hidden dependency, which is bad.

The rule of thumb to prevent such problems is: Include what you use.