C++ 使用C++;模块,是否有理由将函数声明(.hpp文件)与其定义(.cpp文件)分开?

C++ 使用C++;模块,是否有理由将函数声明(.hpp文件)与其定义(.cpp文件)分开?,c++,c++20,c++-modules,C++,C++20,C++ Modules,我习惯于编写没有模块的代码,其中头文件包含如下函数声明: // foo.h class Foo { void bar(); }; 相应的.cpp文件包含以下定义: // foo.cpp #include "foo.h" void Foo::bar() { // ... } 据我所知,这是为了。何时使用,这是否仍然适用?像Java和C#那样将类放在一个包含定义的文件中,速度会一样快吗?如果是这种情况,在使用模块时是否需要.hpp和.cpp文件?有一个很好的讨论解释了模块的概

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

// foo.h 
class Foo
{
    void bar();
};
相应的.cpp文件包含以下定义:

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

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

据我所知,这是为了。何时使用,这是否仍然适用?像Java和C#那样将类放在一个包含定义的文件中,速度会一样快吗?如果是这种情况,在使用模块时是否需要
.hpp
.cpp
文件?

有一个很好的讨论解释了模块的概念


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

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

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


这个想法似乎确实是为了让更像C#/Java机制的东西发挥作用。C++机制在精神上是非常模块化的/艾达的。让机器为我们做更多的工作会很好。

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

在不查看底层细节的情况下轻松地共享和理解一个对象api,这足以让它们保持不变。这是一个20英尺的物体视图,基本上是一个轮廓

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


我认为没有头文件就不可能做到这一点。

我知道的唯一原因是处理循环接口依赖关系

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

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

下面的代码提供了一个不分离声明和定义就无法编译的情况示例:

Foo模块文件

export module Foo;

import module Bar;

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

import module Foo;

export namespace Test {
    class Bar {
    public:
        Foo createFoo() {
            return Foo();
        }
    };
}
Bar模块文件

export module Foo;

import module Bar;

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

import module Foo;

export namespace Test {
    class Bar {
    public:
        Foo createFoo() {
            return Foo();
        }
    };
}
显示了一个示例,说明了在
声明所有权可用的情况下如何解决此问题。本质上,它归结为分离声明和定义


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

您将如何引用函数?如果你把你的头文件和.cpp文件合并在一起,你将得到多重定义函数。我从来没有看到过循环依赖是可取的。相反,我认为这是一个设计问题。从这个意义上说,支持这一点是不是一个好主意?@BitTickler我确实同意你的观点,循环依赖可能表明存在设计问题,但情况并非总是如此——有时很难避免循环依赖。考虑一下有一个节点树的情况,每个节点都希望引用它的父节点和子节点。这将创建一个循环引用。对于几乎所有的父子关系都是如此,例如玩家和物品类,其中玩家类指的是它拥有的物品,物品类指的是它的所有者。