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

Phl*_*das 14 c++ c++20 c++-modules

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

// foo.h 
class Foo
{
    void bar();
};
Run Code Online (Sandbox Code Playgroud)

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

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

void Foo::bar()
{
    // ...
}
Run Code Online (Sandbox Code Playgroud)

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

Qub*_*ub1 10

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

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

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

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

Foo module file

export module Foo;

import module Bar;

export namespace Test {
    class Foo {
    public:
        Bar createBar() {
            return Bar();
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

Bar module file

export module Bar;

import module Foo;

export namespace Test {
    class Bar {
    public:
        Foo createFoo() {
            return Foo();
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

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

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

  • @BitTickler 我同意你的看法,循环依赖可以表明设计问题,但情况并非总是如此 - 有时很难避免循环依赖。考虑一下您有一个节点树的场景,其中每个节点都希望引用其父节点和子节点。这将创建一个循环引用。对于几乎所有父 <-> 子关系都是如此,例如 Player 和 Item 类,其中 Player 类指的是它拥有的项目,而 Item 类指的是其所有者。 (4认同)
  • 我从不认为循环依赖是可取的。相反,我认为这是一个设计问题。从这个意义上说,支持它甚至是一个好主意吗? (3认同)

Ger*_*lny 2

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

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