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

时间:2015-02-05 14:51:01

标签: c++ c++11

这就是我现在所拥有的:

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整数类型

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

2 个答案:

答案 0 :(得分:3)

这里的经验法则是,如果成员变量static(而不是const int),则不能使用成员变量的类内成员初始化,但是有一些例外(仅适用于对你的情况而言)。

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

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

如果你的静态成员是constexpr

,你可以解决这个问题

§9.4.2(2014年11月草案)

  

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

更清楚地解释这个片段: 如果您想尝试使用constexpr来解决问题,那么您的类型必须是“文字”。

文字类型(第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在它自己的类中是不完整的。你有一个类似的不完整的问题。但是,让我们改为指针:

class A{
    private:
        A* member;
}

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

所以现在你想“好吧,我只会让static constexpr CColorf blue指针static constexpr CColorf* blue = new CColorf(0.0f, 0.0f, 1.0f);

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

你不能尝试const因为我们已经解决了原因。

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

所以你运气不好。

答案 1 :(得分:1)

你做不到。

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

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

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

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

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

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