在此类的声明中声明类的实例并就地初始化它

Declaring an instance of a class inside this class' declaration and initializing it in-place

提问人:Violet Giraffe 提问时间:2/5/2015 最后编辑:Violet Giraffe 更新时间:2/7/2015 访问量:155

问:

这就是我现在所拥有的:

class CColorf
{
public:
    CColorf();
    CColorf(float r, float g, float b, float a = 1.0f);

public:
    float r, g, b, a;

// predefined colors
    // rgb(0.0, 0.0, 1.0)
    static const CColorf blue;
};

它适用于 ccolorf.cpp 中的定义,如下所示:blue

CColorf const CColorf::blue = CColorf(0.0f, 0.0f, 1.0f);

这就是我想做的:

class CColorf
{
    ...

// predefined colors
    // rgb(0.0, 0.0, 1.0)
    static const CColorf blue = CColorf(0.0f, 0.0f, 1.0f);
};

但它会产生一个编译错误:

具有类内初始值设定项的静态数据成员必须具有非易失性常量整型

有没有办法避免在这里需要单独的声明和定义?

++ 的 C++11

评论

2赞 Columbo 2/5/2015
声明它。 是文字类型,因此应该可以工作。constexprCColof
0赞 Violet Giraffe 2/5/2015
@Columbo:不应该与表达式(或函数)一起使用,而不是与声明一起使用?constexpr
1赞 Columbo 2/5/2015
...不知道你的意思,但你听起来很困惑。 是一个 decl-specifier(声明说明符),因此只能在声明中使用。constexpr
0赞 Violet Giraffe 2/5/2015
@Columbo:我很困惑。以前从未使用过,几乎不知道这意味着什么。现在正在阅读它。constexpr
1赞 Mike Seymour 2/5/2015
@Columbo:不,问题在于尝试初始化一个不完整的类型,并试图在类中初始化非文字类型的静态成员。没有办法绕过这些限制(可以通过删除构造函数(使其成为聚合)或声明它们来使此类成为字面意思。但它在其定义中仍然是不完整的。constexpr

答:

3赞 AndyG 2/6/2015 #1

这里的经验法则是,如果成员变量是(而不是),则不能使用成员变量的类内成员初始化,但是有一些例外(只是没有适用于您的情况)。staticconst int

在 C++98 标准中,只能进行成员初始化static const int

在 C++11 标准中,您可以对除 C++98 标准以外的所有内容进行成员初始化。static

如果你的静态成员是:constexpr

§ 9.4.2(2014 年 11 月草案)

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

为了更清楚地解释这个片段: 如果你想尝试用 来解决问题,你的类型必须是“字面意思”。constexpr

文本类型 (§ 3.9.10):

  • 有一个“微不足道”的析构函数
  • 只有常量表达式构造函数
  • 仅具有文本类型基类和数据成员
  • 或者是聚合类型
  • 或者是 、标量(例如,)、引用或文本类型的数组voidint

如果出现以下情况,析构函数是“微不足道的”:

  • 它是编译器生成的(即你没有定义一个)
  • 每个非静态成员对象都有一个微不足道的析构函数

考虑到所有这些,你可能会看一下你的代码,然后想“嗯,好吧,我可以把我所有的构造函数都变成,然后改成,我很好。constexprstatic const CColorf bluestatic constexpr CColorf blue

但是,在声明静态时,您的类是“不完整的”。让我们考虑以下示例:

class A{
    private:
        A member;
}

now 的每个实例都有一个 的实例。编译器为多少字节分配?它说不出来。无限多,也许是由于递归。A 在它自己的类中是不完整的。你有一个类似的不完整问题。但是,让我们改为将其设置为指针:AAA

class A{
    private:
        A* member;
}

现在这很容易,因为是一个指针类型,编译器知道它的大小。A*

所以现在你会想“好吧,我就做一个指针static constexpr CColorf bluestatic constexpr CColorf* blue = new CColorf(0.0f, 0.0f, 1.0f);

但是你不能,因为操作员不是.newconstexpr

你不能尝试,因为我们已经讨论过为什么。const

所以也许你考虑过让运算符重载,但你也做不到newconstexpr

所以你不走运。

评论

0赞 Violet Giraffe 2/6/2015
请参阅迈克·西摩(Mike Seymour)对该问题的评论。你的建议不是答案。
0赞 Violet Giraffe 2/6/2015
我问题的答案要么是一段代码,说明如何做我想做的事,要么是 3 个字:“这是不可能的”。你的“答案”都不是;它说“如果你的静态成员是”,你可以解决这个问题“,但事实上,让我的静态成员并不能解决问题。更不用说标准中那段非人类可读的文本几乎没有告诉我任何事情。constexprconstexpr
0赞 AndyG 2/6/2015
@VioletGiraffe:我很抱歉。有时,一个人清楚的事情对另一个人来说可能并不那么清楚。我在回答中添加了更多细节,希望能让事情更清楚。
1赞 Mike Seymour 2/6/2015 #2

你不能这么做。

错误消息意味着您正在编译为 C++03,其中只能在其声明中初始化整数类型的常量静态成员;因此,您不能对任何类类型执行此操作。

C++11放宽了规则,但仍有限制:

  • 键入的值必须为文本。你可以通过制作构造函数来使这个类型成为文字;但constexpr
  • 类型必须是完整的,并且类在其定义中不完整(成员定义中除外)
  • 成员不得使用 ODR;也就是说,您只能将其用作右值表达式,而不能获取其地址或创建对它的引用。

虽然第一点是可以固定的,第三点只会限制你可以对成员做什么,而不是你是否可以定义它,但第二点使它变得不可能。您必须以通常的方式定义变量,在单个翻译单元的类之外。

如果要将所有内容保留在类定义中,并且希望将值用于帮助编译时优化,则可以定义一个函数而不是变量

static CColorf blue() {return CColorf(0.0f, 0.0f, 1.0f);}