22

我习惯于编写没有模块的代码,其中头文件包含函数声明,例如:

// foo.h 
class Foo
{
    void bar();
};

并且相应的 .cpp 文件包含定义:

// foo.cpp
#include "foo.h"

void Foo::bar()
{
    // ...
}

据我所知,这样做是为了减少编译时间和减少依赖。什么时候使用模块,这仍然适用吗?将类与 Java 和 C# 的定义方式放在一个文件中是否一样快?如果是这种情况,使用模块时是否需要.hpp和文件?.cpp

4

4 回答 4

10

正如模块提案目前所代表的那样,我知道的唯一原因是处理循环接口依赖关系。

如果一个程序由模块组成并且它没有将函数声明与定义分开,那么所有模块文件都将是模块接口(与模块实现相反)。如果要将它们与头文件和代码文件进行比较,可以将模块接口视为头文件(.hpp),将模块实现视为代码(.cpp)文件。

不幸的是,模块提案不允许循环模块接口依赖。而且由于您的程序现在完全由模块接口组成,因此您将永远无法拥有两个以任何方式相互依赖的模块(这可能会proclaimed ownership在将来的声明中得到改进,但目前不支持) . 解决循环模块接口依赖关系的唯一方法是将声明和定义分开并将循环导入放在模块实现文件中,与循环模块接口依赖关系相反,允许循环模块实现依赖关系。

以下代码提供了一个示例,说明如果不分离声明和定义就无法编译:

Foo module file

export module Foo;

import module Bar;

export namespace Test {
    class Foo {
    public:
        Bar createBar() {
            return Bar();
        }
    };
}

Bar module file

export module Bar;

import module Foo;

export namespace Test {
    class Bar {
    public:
        Foo createFoo() {
            return Foo();
        }
    };
}

本文展示了一个示例,说明如何在proclaimed ownership声明可用的情况下解决此问题。本质上,它归结为分离声明和定义。

在一个完美的世界中,编译器将能够处理这种情况,但是,唉,据我所知,当前提议的模块实现不支持它。

于 2018-07-19T21:29:36.737 回答
3

仍然有很多理由使用头文件。

在不查看底层细节的情况下共享和理解对象 api 的便利性足以让它们留在身边。这是一个 20 英尺的物体视图,本质上是一个轮廓。

如果您要销售一个库,您将包括一个头文件,以及一个存档文件或一个共享库。这样,您可以在不损害产品 IP 的情况下保持信息的专有性,并且您的客户可以包含为他们的目标编译的二进制文件。

我不相信没有头文件这是可能的。

于 2018-03-24T05:28:14.597 回答
2

这里有一个很好的讨论解释模块的想法。

简而言之,您是对的,将不再需要头文件和实现文件之间的分离。该#include指令将被指令替换import,并且在编译时模块将提供所需的信息,否则这些信息将包含在包含的头文件中。

于 2017-05-10T16:55:57.883 回答
1

这个习语的另一个用法是从 C 继承而来的;即使对于不依赖于其他翻译单元的翻译单元,它也是一种方便的前向声明方式。

使用预编译头文件并没有真正成为一件重要的事情,直到 C++ 对头文件的扩展使用使得出于性能原因必须这样做(尽管有一些非常大的老式头文件,如 windows.h)。

这个想法似乎是要让 C#/Java 机制发挥作用。C++ 机制在精神上非常 Modula/ADA。让机器为我们做更多的工作会很好。

于 2018-03-13T22:48:48.423 回答