为什么“int const B::bsm1a;” 不考虑重新定义吗?

SAb*_*shi 2 c++ visual-studio

#include <iostream>
struct B {
public:
    static int const bsm1a{ 0xf };
};
int const B::bsm1a; // 1) Why is this NOT considered a redefinition?
                    // 2) What does this line actually do here?

int main()
{
    std::cout << "Hello World!\n";
}
Run Code Online (Sandbox Code Playgroud)
#include <iostream>
struct B {
public:
    static int const bsm1a{ 0xf };
    static int const bsm1a; // Considered as a redefinition here and throws error C2086
};

int main()
{
    std::cout << "Hello World!\n";
}
Run Code Online (Sandbox Code Playgroud)

我的问题(嵌入在代码中):

  1. 为什么这不被视为重新定义?
  2. 这条线实际上在这里做什么?

Not*_*mer 7

这是我阅读 C++ 标准时得到的信息。如果我的解释有误,请指正。

\n

第一种情况的情况相当有趣。这归结为 C++ 中 ofdeclaration和的含义definition。如果您深入研究 C++ 标准,您会发现此主题的一部分:

\n
\n

(\xc2\xa76.2) 声明声明的每个实体也由该声明定义,除非:

\n
\n

(\xc2\xa76.2.3) 它在类定义中声明一个非内联静态数据成员 (11.4, 11.4.8)

\n
\n
\n

这告诉我们该行static int const bsm1a{ 0xf };不是定义而是声明。

\n

在类内部定义static const数据成员过去是不允许的,因为当静态成员是类定义的一部分并包含在多个不同的翻译单元中时,它将违反单一定义规则(您现在可以在 C++17 后使用变量来执行此操作inline) )。所以过去的写法是

\n
struct Foo {\npublic:\n    static const std::string str; //Declaration\n};\nconst std::string Foo::str = "Hello World\\n"; //Definition\n
Run Code Online (Sandbox Code Playgroud)\n

但是,允许static const在类定义中根据 \xc2\xa711.4.8.2.4 声明整型或枚举类型的变量

\n
\n

如果非易失性非内联 const 静态数据成员是整型或枚举类型,则其在类定义中的声明可以指定大括号或等于初始化程序,其中作为赋值表达式的每个初始化程序子句都是常量表达

\n
\n

static int const bsm1a{ 0xf };符合该描述,因此是允许的,但它仍然是一个声明。

\n

此时,B::bsm1a已声明但未定义。您可以给它一个整数或 an 值enum,因为编译器可以将其放入寄存器中,而不会实际为其分配内存。

\n
#include <iostream>\n\nstruct B {\n    static const int bsm1a{0xf}; // Compiler keeps track of the 0xf internally\n};\n\nint main()\n{\n    std::cout << &B::bsm1a << "\\n"; //Whoops, this is not defined and there is no memory allocated to it, so taking its address is impossible (you\'ll get a linker error)\n}\n
Run Code Online (Sandbox Code Playgroud)\n

当您注释掉std::cout并检查程序集时,没有引用B::bsm1a. 当您稍后编写时,int const B::bsm1a;这不是声明而是定义。只有此时变量才被定义并且内存被分配给它。当您检查下面代码的汇编时,您将看到现在“15”出现在最终汇编中,并调用&B::bsm1a实际地址其内存位置(movl $B::bsm1a, %esi

\n
#include <iostream>\n\nstruct B {\n    static const int bsm1a{0xf}; \n};\nconst int B::bsm1a; // It is now defined and has memory assigned to it\n\nint main()\n{\n    std::cout << &B::bsm1a << "\\n"; // We can now take its address\n}\n
Run Code Online (Sandbox Code Playgroud)\n

在第二种情况下,你还有另一个问题。

\n
\n

(\xc2\xa711.4.5) 成员不得在成员规范中声明两次,除非

\n
\n

(\xc2\xa75.1) 可以声明嵌套类或成员类模板,然后再定义,并且\n(\xc2\xa75.2) 可以使用不透明枚举声明引入枚举,然后使用以下命令重新声明枚举说明符

\n
\n
\n

除非它满足这两个标准,否则您根本不允许在类成员规范中重新声明它。因此,例如以下内容是合法的:

\n
struct Foo\n{\npublic:\n    struct X; // declaration\n    struct X; // redeclaration, allowed\n};\n
Run Code Online (Sandbox Code Playgroud)\n

但由于您static const int不是这些类型之一,因此这是非法的。

\n