在头文件中声明constexpr类并在单独的.cpp文件中定义,这种做法可行吗?

92

我有一个类Dimension,像我所有的类一样,我在文件 Dimension.h 中定义它:

class Dimension
{
public:

    constexpr Dimension() noexcept;

    constexpr Dimension(int w, int h) noexcept;

    int width;
    int height;

};

我原本以为可以像我所有的课程一样,将定义放在单独的 Dimension.cpp 文件中:

#include "Dimension.h"

constexpr Dimension::Dimension() noexcept : width(0), height(0) {}

constexpr Dimension::Dimension(int w, int h) noexcept : width(w), height(h) {}

但是当我尝试使用这个类时,编译器告诉我:

警告:使用但未定义内联函数'constexpr Dimension::Dimension()'

在链接时出现:

对'pong::graphics::Dimension::Dimension()'的未定义引用

(另一个构造函数同样如此)

如果我像这样在头文件中定义这个类:

class Dimension
{
public:

    constexpr Dimension() noexcept : width(0), height(0) {}

    constexpr Dimension(int w, int h) noexcept : width(w), height(h) {}

    int width;
    int height;

};

如果省略 .cpp 文件,一切都能正常运行。

我正在使用GCC 4.9.2。为什么独立定义无法工作?


9
constexpr 函数的核心目的是允许在编译时计算函数。如果编译器看不到该函数的主体内容,则很难实现这一点。因此,需要让编译器看到整个函数的主体内容。 - Igor Tandetnik
我已经怀疑了,但我不太确定能否解释清楚(或许需要参考资料)为什么会发生这种情况。在谷歌上搜索时我没有找到任何相关信息,因此我认为提出一个新问题是合适的。 - Timo Türschmann
1
“但我不太确定是否能解释清楚(或许需要参考资料)为什么会发生这种情况…” 重新表述一下 @IgorTandetnik 的评论:编译器需要在使用 constexpr 时完全展开,以便插入正确的表达式。链接阶段太晚了。 - πάντα ῥεῖ
6
如果您坚持要求章节和条款:7.1.5/2 constexpr 函数和 constexpr 构造函数隐式地是内联的。 3.2/4 内联函数应该在每个使用其 odr 的翻译单元中进行定义。 - Igor Tandetnik
2个回答

100
如果constexpr函数不在头文件中定义,编译器在编译所有其他源文件时无法看到这些constexpr函数的定义。
显然,如果它无法看到这些函数的定义,它就无法执行在编译时计算它们所需的步骤。因此,所有constexpr函数必须在使用它们的每个地方进行定义。
感谢@IgorTandetnik: [dcl.constexpr] §7.1.5/2

constexpr函数和constexpr构造函数隐式具有内联特性。

[basic.def.odr] §3.2/4

内联函数应在使用odr的每个翻译单位中定义。


24
您所询问的内容可以实现,但有一个重要的限制:在定义constexpr函数的翻译单元(即源文件)内部才能调用该函数。这对于您提供的示例来说并不合适,因为构造函数应该是类的公共接口的一部分。但是,在其他情况下它非常有用,例如将私有方法定义为constexpr,然后在需要在编译时知道其返回值的表达式中使用它们,例如模板实例化、switch语句标签等等。

阅读第一个答案,然后查看编译源代码时,在.h和.cpp文件中定义了constexpr函数,这让人非常困惑。上面接受的解决方案实际上是不正确的,因为它省略了这个重要细节。 - okovko
@okovko所接受的解决方案说:“所有constexpr函数必须在使用它们的地方定义。”这就是你的“重要细节”。 - Matthew M.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接