【问题标题】:Declaring an instance of a class inside this class' declaration and initializing it in-place在这个类的声明中声明一个类的实例并就地初始化它
【发布时间】:2015-04-05 11:42:29
【问题描述】:

这就是我现在拥有的:

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);
};

但是会产生编译错误:

具有类内初始化程序的静态数据成员必须具有非易失性 const 整数类型

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

【问题讨论】:

  • 声明它constexprCColof 是文字类型,因此应该可以工作。
  • @Columbo:constexpr 不应该与表达式(或函数)一起使用,而不是声明吗?
  • ...不知道你的意思,但你听起来很困惑。 constexpr 是一个 decl-specifier(声明说明符),因此只能在声明中使用。
  • @Columbo:我很困惑。以前从未使用过constexpr,几乎不知道它是什么意思。立即阅读。
  • @Columbo:不,问题是试图初始化一个不完整的类型,并试图在类中初始化一个非文字类型的静态成员。无法绕过这些限制(可以通过删除构造函数(使其成为聚合)或将其声明为 constexpr 来使此类成为文字。但在其定义中仍然是不完整的。)

标签: c++ c++11


【解决方案1】:

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

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

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

如果你的静态成员是constexpr,你可以解决这个问题:

§ 9.4.2(2014 年 11 月草案)

如果非易失性 const static 数据成员是整数或枚举类型,则其在类中的声明 定义可以指定一个 brace-or-equal-initializer,其中每个 initializer-clause 是一个 assignment-expression 是一个常数表达式 (5.20)。文字类型的 static 数据成员可以在 constexpr 说明符的类定义;如果是这样,它的声明应指定一个brace-or-equal-initializer 其中每个 initializer-clause 是一个 assignment-expression 是一个常量表达式。 [ 注意:在这两个 在这些情况下,成员可能出现在常量表达式中。 — 尾注] 成员仍应被定义 如果在程序中使用了 odr-used (3.2) 并且命名空间范围定义不应在命名空间范围内 包含一个初始化器

为了更清楚地解释这个 sn-p: 如果您想尝试使用constexpr 解决问题,您的类型必须是“literal”。

文字类型(§ 3.9.10):

  • 有一个“微不足道”的析构函数
  • 只有常量表达式构造函数
  • 只有文字类型基类和数据成员
  • 或者是aggregate type
  • 或者是void、标量(例如int)、引用或文字类型数组

如果满足以下条件,析构函数是“微不足道的”:

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

鉴于所有这些,您可能会查看您的代码并想“嗯,我可以让我的所有构造函数constexpr,然后将static const CColorf blue 更改为static constexpr CColorf blue,我很好。”

但是,在您声明静态时,您的类是“不完整的”。让我们想想下面的example

class A{
    private:
        A member;
}

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

class A{
    private:
        A* member;
}

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

所以现在你想“好吧,我就让 static constexpr CColorf bluestatic constexpr CColorf* blue = new CColorf(0.0f, 0.0f, 1.0f); 这样的指针

但你不能,因为new 运算符不是constexpr

你不能尝试const,因为我们已经讨论了原因。

所以也许您考虑将new 运算符重载为constexpr,但you can't do that either

所以你运气不好。

【讨论】:

  • 查看 Mike Seymour 对该问题的评论。您的建议不是答案。
  • 我的问题的答案要么是一段代码,显示如何做我想做的事,要么是三个字:“这是不可能的”。你的“答案”都不是;它说“如果你的静态成员是constexpr,你可以解决这个问题”,但实际上让我的静态成员constexpr 并不能解决问题。更不用说标准中的那段非人类可读的文本几乎没有告诉我任何事情。
  • @VioletGiraffe:我很抱歉。有时对一个人来说清楚的事情可能对另一个人来说不是那么清楚。我在回答中添加了更多细节,希望能让事情更清楚。
【解决方案2】:

你不能这样做。

错误消息暗示您正在编译为 C++03,其中只有整数类型的常量静态成员可以在其声明中初始化;所以你不能对任何类类型这样做。

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

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

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

如果您想保留类定义中的所有内容,以及可用于帮助编译时优化的值,您可以定义一个函数而不是变量

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

【讨论】:

    猜你喜欢
    • 2014-10-24
    • 1970-01-01
    • 1970-01-01
    • 2015-04-27
    • 1970-01-01
    • 2013-07-06
    • 1970-01-01
    • 2023-03-29
    • 1970-01-01
    相关资源
    最近更新 更多