0

我正在使用 GCC 4.8.2:

$ g++ --version                          
g++ (GCC) 4.8.2
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

我有这个类定义:

#pragma once

#include "SpreadsheetCell.h"

class Spreadsheet 
{
    public:
        Spreadsheet(int inWidth, int inHeight);
        Spreadsheet(const Spreadsheet& src);
        ~Spreadsheet();

        int getId() const;
        int getWidth() const;
        int getHeight() const;
        void setCellAt(int x, int y, const SpreadsheetCell& cell);
        SpreadsheetCell getCellAt(int x, int y) const;

        Spreadsheet& operator=(const Spreadsheet& rhs);
    private:
        bool inRange(int val, int upper) const;
        void copyFrom(const Spreadsheet& src);
        void freeMemory();

        int mWidth, mHeight, mId;
        SpreadsheetCell** mCells;

        static int sCounter = 0;
};

当我尝试编译它时,我得到:

$ make SpreadsheetTest && SpreadsheetTest
g++ -Wall -g -std=c++11 -c Spreadsheet.cpp
In file included from Spreadsheet.cpp:3:0:
Spreadsheet.h:27:31: error: ISO C++ forbids in-class initialization of non-const static member ‘Spreadsheet::sCounter’
         static int sCounter = 0;
                               ^
Makefile:11: recipe for target 'Spreadsheet.o' failed
make: *** [Spreadsheet.o] Error 1

奇怪的是,如果我static从声明中删除修饰符sCounter,它会正常编译。

到底是怎么回事?

编辑:

自 GCC 4.7 起,此功能似乎可用:http: //gcc.gnu.org/projects/cxx0x.html

编辑2:

我正在尝试找到官方参考,但我从 Gregoire、Solter & Kleper 的“Professional C++”-第 2 版一书中获取了这段代码(第 7 章,第 181 页,“静态数据成员”小节)和它应该工作。

使用 C++11,这就是您需要做的所有事情。如果您使用 C++ pior to C++11,它会有点笨拙 [...]

然后它建议我做传统的方式。

作者错了吗?

4

3 回答 3

3

再次阅读错误信息:

ISO C++ 禁止非 const静态成员 'Spreadsheet::sCounter'的类内初始化static int sCounter = 0;

如果你想static在类定义中初始化一个成员,它必须是const,这就是它的全部。

普通类成员在构造该类的对象时被初始化。您可以将类内初始化器视为成员初始化器列表的语法糖(或将它们视为类似于默认函数参数)。您仍然可以覆盖它并以旧方式(在构造函数中)使用不同的表达式初始化它们。

静态成员是不同的。它们不属于任何类实例,因此需要为它们分配内存并单独初始化。由于它们具有静态存储持续时间,因此它位于程序启动时的某个位置。

常量静态成员是一个例外。由于它们不能被写入,标准允许您在类声明中初始化它们,它们基本上被视为一个值,而不是一个对象。也就是说,直到你对它们做了一些需要将它们存储在某个地方的事情,例如获取它们的地址。这在 C++11 中没有改变。

标准引用,来自 n3337 草案,第 9.4.2 章(强调我的):

2 静态数据成员在其类定义中的声明不是定义,并且可能是除 cv 限定的 void 之外的不完整类型。静态数据成员的定义应出现在包含该成员的类定义的命名空间中。在命名空间范围的定义中,静态数据成员的名称应使用 :: 运算符由其类名限定。静态数据成员定义中的初始化表达式在其类(3.3.7)的范围内。[...]

3如果非易失性 const 静态数据成员是整数或枚举类型,它在类定义中的声明可以指定一个大括号或等式初始化器,其中每个作为赋值表达式的初始化器子句都是一个常量表达式(5.19 )。文字类型的静态数据成员可以在类定义中用 constexpr 说明符声明;如果是这样,它的声明应指定一个大括号或等式初始化器,其中作为赋值表达式的每个初始化器子句都是一个常量表达式。[注意:在这两种情况下,成员都可能出现在常量表达式中。—尾注]如果该成员在程序中被 odr-used (3.2) 并且命名空间范围定义不应包含初始化程序,则该成员仍应在命名空间范围内定义。

于 2013-11-02T17:57:44.033 回答
1

将静态定义移出类声明:

class Spreadsheet 
{
   ...
   static int sCounter;
};

int Spreadsheet::sCounter = 0;
于 2013-11-02T17:52:35.050 回答
1
  • constC++98 允许对整型或枚举类型的静态成员进行类内初始化。

  • C++11 还允许非静态成员的类内初始化程序。

您的代码不属于任何一个类别。它不应该在 C++98 中编译,也不应该在 C++11 中编译。在 C++ 中,不允许为非常量静态成员提供类内初始化程序。

另请注意,原始 C++98 功能尚未扩展到非整数非枚举类型

struct S {
  static const double d = 5; // still an error even in C++11
};

顺便说一句,静态常量和非静态类数据成员的类内初始化程序的语义和背后的基本原理是完全不同的。尽管在语法上相似,但它们实际上是两个不同的特征,而不是前者对后者的扩展。

  • 静态常量数据成员的类内初始化器的存在是为了促进“早期”积分常量表达式,即它们在类定义点将静态常量转换为 ICE(与静态成员定义的后期点相反)。

  • 非静态数据成员的类内初始化器有助于在构造函数中隐式初始化类成员。换句话说,在 C++11 中看起来像非静态数据成员的类内初始化器并没有真正初始化任何东西。(此时没有什么可以初始化的。)所有这些类内初始化器所做的就是为编译器提供稍后用于生成类构造函数的信息。当您真正开始定义该类类型的对象时,这些构造函数甚至会在以后工作。

于 2013-11-02T19:17:35.493 回答