C++ 类静态 constexpr 的链接器错误

C++ Linker Error With Class static constexpr

本文关键字:链接 错误 constexpr 静态 C++      更新时间:2023-10-16

我正在用g++-4.6.1 --std=c++0x编译以下简单的程序:

#include <algorithm>
struct S
{
    static constexpr int X = 10;
};
int main()
{
    return std::min(S::X, 0);
};

我收到以下链接器错误:

/tmp/ccBj7UBt.o: In function `main':
scratch.cpp:(.text+0x17): undefined reference to `S::X'
collect2: ld returned 1 exit status

我意识到内联定义的静态成员没有定义符号,但我有一个(可能有缺陷的(印象,即使用 constexpr 告诉编译器始终将符号视为表达式;因此,编译器会知道传递对符号的引用是不合法的S::X(出于同样的原因,你不能引用文字10(。

但是,如果将 S 声明为命名空间,即"命名空间 S"而不是"结构 S",则一切链接正常。

这是一个g++错误,还是我仍然必须使用技巧来解决这种烦恼?

我不认为这是一个错误。如果将constexpr更改为 const ,它仍然失败,并显示完全相同的错误。

您已经声明了S::X,但未在任何地方定义它,因此没有存储。如果你对它做了任何需要知道它地址的事情,那么你也需要在某个地方定义它。

例子:

int main() {
      int i = S::X; // fine
      foo<S::X>(); // fine
      const int *p = &S::X; // needs definition
      return std::min(S::X, 0); // needs it also
}

这样做的原因是,constexpr可以在编译时进行评估,但不需要这样计算,并且同样可以在运行时进行。它没有指示"编译器始终将符号视为表达式">,它暗示如果编译器愿意,这样做是明智和允许的。

此问题

已在 C++17 中修复。

https://en.cppreference.com/w/cpp/language/static:

如果静态数据成员被声明为 constexpr,则它是隐式内联的 并且不需要在命名空间范围内重新声明。这 不带初始值设定项的重新声明(以前如图所示需要 以上(仍允许,但已弃用。

错误的原因已经解释过了,所以我只是添加一个解决方法。

return std::min(int(S::X), 0);

这将创建一个临时的,因此std::min可以引用它。

您还需要为结构(或类(之外的 constexpr 成员提供定义,但这次没有其值。看这里: https://en.cppreference.com/w/cpp/language/static

#include <algorithm>
struct S
{
    static constexpr int X = 10;
};
constexpr int S::X;
int main()
{
    return std::min(S::X, 0);
};

在C++标准(最新工作草案(中,它说:

具有命名空间范围 (3.3.6( 的名称具有内部链接,如果它是 [...] 的名称,该变量显式声明为 constconstexpr,并且既未显式声明extern,也没有先前声明为具有外部链接 [...]。

"联动"的定义如下:

当名称可能表示相同的对象、引用、函数、类型、模板、 命名空间或值作为另一个作用域中的声明引入的名称:

— 当名称具有外部链接时,它所表示的实体可以用范围中的名称引用 其他翻译单元或来自同一翻译单元的其他范围。

— 当名称具有内部链接时,它所表示的实体可以用其他作用域的名称引用 在同一个翻译单元中。

— 当名称没有链接时,它所表示的实体不能由其他范围的名称引用。

因此,在namespace S的情况下,它将具有外部链接,在struct S的情况下,它将具有内部链接

具有外部链接的符号需要在某个翻译单元中明确定义符号。

你对constexpr的理解是错误的。 声明的左值 constexpr 仍然是一个左值,并且声明了一个函数 constexpr仍然是一个函数。 当一个函数有一个引用参数,并传递一个左值,即语言要求引用引用该左值,并且不引用任何内容还。 (当应用于类型 int 的变量时,有constexpr和普通之间真的几乎没有区别 const .(