为什么可以初始化静态const变量内联而不是普通静态(C ++)

时间:2012-02-09 22:32:17

标签: c++

如果我要这样做

class Gone
{
    public:
    static const int a = 3;
}

它有效但是如果

class Gone
{
    public:
    static int a = 3;
}

它给出了编译错误。现在我知道为什么第二个不起作用,我只是不知道第一个为什么会这样做。

提前致谢。

5 个答案:

答案 0 :(得分:6)

此技巧仅适用于常量编译时表达式。请考虑以下简单示例:

#include <iostream>

class Foo {
public:
    static const int bar = 0;
};

int main()
{
    std::cout << Foo::bar << endl;
}

它工作得很好,因为编译器知道Foo::bar为0且永远不会改变。因此,它优化了整个事物。

然而,一旦你拿走那个变量的地址,整个事情就会破坏:

int main()
{
    std::cout << Foo::bar << " (" << &Foo::bar << ")" << std::endl;
}

链接器发送给你修复程序,因为编译时常量没有地址。

现在,您示例中的第二种情况不起作用,因为非常量变量不能是常量编译时表达式。因此,您必须在某处定义它,并且不能在初始化中分配任何值。

顺便提一下,C ++ 11有constexpr。您可以查看Generalized constant expressions wiki(或C ++ 11标准:-))以获取更多信息。

另外,请注意 - 使用某些工具链时,即使您从未获取这些变量的地址,也无法在第一个示例中列出优化时关闭的程序。我认为Boost中有一个BOOST_STATIC_CONSTANT宏来解决这个问题(不确定它是否有效,因为我认为即使使用那个宏,也会看到一些旧gcc的链接失败)。

答案 1 :(得分:2)

static const int声明是合法的,因为您声明的是常量,而不是变量。 a不作为变量存在 - 编译器可以自由地对其进行优化,并在出现3的引用的任何地方将其替换为声明的值Gone::a。 C ++允许在这种限制情况下进行静态初始化,它是一个整数常量。

您可以找到更多详细信息,包括ISO C ++标准引文here

答案 2 :(得分:2)

变量的初始化必须在定义点进行,而不是在一般情况下声明。在类括号内,您只有声明,并且您需要在单个翻译单元 * 中提供定义

// can be in multiple translation units (i.e. a header included in different .cpp's)
struct test {
   static int x;    // declaration
   static double d; // declaration
};
// in a single translation unit in your program (i.e. a single .cpp file)
int test::x = 5;       // definition, can have initialization
double test::d = 5.0;  // definition

话虽如此,static积分常量(只有积分常数)有一个例外,你可以在声明中提供常量的值。异常的原因是它可以用作编译时常量(即定义数组的大小),这只有在编译器看到在需要的所有翻译单元中常量的值。

struct test {
   static const int x = 5;  // declaration with initialization
};
const int test::x;          // definition, cannot have initialization

回到原来的问题:

  • 为什么不允许非const整数?
  • 因为初始化发生在定义而非声明
  • 为什么允许积分常数?
  • 以便它可以在所有翻译单元中用作编译时常量

* 只要程序中使用成员属性,实际规则就需要定义。现在 used 的定义在C ++ 03中有点棘手,因为它可能不是那么直观,例如将该常量用作 rvalue 并不构成根据标准使用。在C ++ 11中,术语 used 已被 odr-used 替换,以避免混淆。

答案 3 :(得分:0)

我似乎记得最初(ARM)它是不被允许的,我们曾经使用enum来定义类声明中的常量。

显式引入了const case,以支持头文件中值的可用性,以便在常量表达式中使用,例如数组大小。

(如果我有错误请注释),严格来说你还需要定义这个值:

const int Gone::a;

遵守One Definition Rule。但是,在实践中,您可能会发现编译器优化了对Gone::a地址的需求,并在没有它的情况下逃脱。

如果你采取:

const int* b = &Gone::a;

然后你可能会发现你确实需要这个定义。

参见标准,$ 9.4.2:

ISO 1998:

  

“4如果静态数据成员是const integral或const枚举   type,它在类定义中的声明可以指定一个   constantinitializer,它应该是一个整数常量表达式   (5.19)。在这种情况下,成员可以出现在整数常量中   范围内的表达。该成员仍应定义为   命名空间范围,如果它在程序和命名空间范围中使用   定义不应包含初始化程序。“

c ++ 11的草案:

  

“3如果静态数据成员是const有效文字类型,则为   类定义中的声明可以指定一个常量初始化器   具有初始化子句的大括号括号的大括号   积分常数表达式。一个有效的静态数据成员   文字类型可以在类定义中声明   constexpr说明符;如果是的话,其声明应指明一个   常量初始化器支持或等于初始化器   initializerclause是一个整数常量表达式。同时   在这些情况下,成员可能出现在整数常量表达式中。   如果使用该成员,则仍应在命名空间范围内定义该成员   在程序和命名空间范围定义中不应包含   初始值设定“。

我不完全确定这涵盖了什么,但我认为这意味着我们现在可以使用相同的习惯用于浮点和可能的字符串文字。

答案 4 :(得分:0)

静态const在类定义中定义,因为使用代码的每个人都需要在编译时知道值,而不是链接时间。普通静态实际上只在类定义中声明,但在一个转换单元中定义一次。