我有以下工作代码:
#include <string>
#include <iostream>
class A {
public:
const std::string test = "42";
//static const std::string test = "42"; // fails
};
int main(void){
A a;
std::cout << a.test << 'n';
}
是否有充分的理由不可能使测试成为static const
?我知道在c++11之前,它受到了标准的限制。我认为c++11在类初始化中引入是为了让它更友好一点。我也没有这样的语义可用于积分类型,因为相当长的一段时间。
当然,它以const std::string A::test = "42";
的形式与类外初始化一起工作
我想,如果你能让它成为非静态的,那么问题就出在两者中的一个。在类范围外初始化它(通常const
是在对象实例化期间创建的)。但我不认为这是问题,如果你正在创建一个独立于类中任何其他成员的对象。第二个是对静态成员有多个定义。例如,如果它包含在多个.cpp
文件中,登录到多个对象文件中,则链接器在将这些对象链接在一起(例如,链接到一个可执行文件中)时会遇到问题,因为它们将包含同一符号的副本。据我所知,这完全等同于在标头中的类声明下提供类外权限,然后在多个位置包含这个公共标头的情况。我记得,这会导致链接器错误。
然而,现在处理这个问题的责任转移到了用户/程序员身上。如果想要有一个带有static
的库,他们需要提供一个类外定义,将其编译到一个单独的对象文件中,然后将所有其他对象链接到这个对象,因此只有符号的二进制定义的一个副本。
我阅读了中的答案:我们是否仍然需要单独定义静态成员,即使它们是在类定义中初始化的?以及为什么可以';t我初始化类中的非常量静态成员或静态数组?。
我仍然想知道:
- 这只是一个标准的东西,还是背后有更深层次的推理
- 这可以通过
constexpr
和用户定义来解决吗文字机制。clang和g++都表示变量不能具有非文字类型。也许我可以做一个。(也许出于某种原因,这也是个坏主意) - 链接器只包含一个符号?由于它是
static const
,所以所有的都应该是二进制精确的不可变副本
如果我遗漏或理解错误,也请发表评论。
你的问题有两部分。标准是怎么说的?为什么会这样?
对于类型为const std::string
的静态成员,它需要在类说明符之外定义,并且在其中一个转换单元中有一个定义。这是一个定义规则的一部分,并在C++标准的第3条中进行了规定。
但为什么呢?
问题是,具有静态存储持续时间的对象在最终程序映像中需要唯一的静态存储,因此它需要从一个特定的翻译单元链接。类说明符在一个翻译单元中没有home,它只是定义类型(要求在使用它的所有翻译单元中定义相同)。
常量积分不需要存储的原因是它被编译器用作常量表达式,并在使用点内联。它永远不会出现在程序图像中。
然而,像std::string
这样具有静态存储持续时间的复杂类型需要存储,即使它们是const
。这是因为它们可能需要动态初始化(在进入main之前调用它们的构造函数)。
您可能会争辩说,编译器应该在使用对象的每个转换单元中存储具有静态存储持续时间的对象的信息,然后链接器应该在链接时将这些定义合并到程序映像中的一个对象中。我对为什么不这样做的猜测是,这需要来自链接器的太多智能。