我正在使用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”而不是“struct S”,则一切都链接正常。
这是g++
错误还是我仍然需要使用技巧来解决这个烦恼?
答案 0 :(得分:32)
我不认为这是一个错误。如果您将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
可以在编译时进行评估,但不是必需进行评估,并且同样可以在运行。它没有指示“编译器始终将符号视为表达式”,它暗示如果编译器感觉它是合理的,也是允许的。
答案 1 :(得分:12)
已经解释了出错的原因,所以我只想添加一个解决方法。
return std::min(int(S::X), 0);
这会创建一个临时的,因此std::min
可以引用它。
答案 2 :(得分:5)
此问题已在C ++ 17中修复。
https://en.cppreference.com/w/cpp/language/static:
如果将静态数据成员声明为constexpr,则将其隐式内联 并且无需在命名空间范围内重新声明。这个 没有初始化程序的重新声明(以前是必需的,如图所示) 以上)仍被允许,但已弃用。
答案 3 :(得分:4)
在C ++标准(latest working draft)中,它说:
具有命名空间作用域(3.3.6)的名称具有内部链接,如果它是显式声明为
const
或constexpr
且未明确声明extern
的变量的名称。 1}}也未声明具有外部联系[...]。
&#34;连杆&#34;定义如下:
当一个名称可能表示相同的对象,参考,功能,类型,模板, 名称空间或值作为另一个范围内的声明引入的名称:
- 当名称具有外部链接时,其表示的实体可以通过范围中的名称来引用 其他翻译单位或同一翻译单位的其他范围。
- 当名称具有内部链接时,其表示的实体可以通过其他范围的名称引用 在同一个翻译单元。
- 如果名称中没有无链接,则其表示的实体不能通过其他范围的名称引用。
因此,对于namespace S
,它将具有外部链接,如果是struct S
,它将具有内部链接。< / p>
具有外部链接的符号需要在某些翻译单元中明确定义符号。
答案 4 :(得分:4)
您还需要为struct(或类)外部的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);
};
答案 5 :(得分:1)
您对constexpr
的理解是错误的。宣布左值
constexpr
仍然是左值,并且声明了一个函数
constexpr
仍然是一个功能。当一个功能有
一个引用参数,它传递一个左值,即语言
要求引用引用该左值,而不是任何内容
其他。 (当应用于int
类型的变量时,有
constexpr
和普通之间的差别确实很小
const
。)