提问人:Violet Giraffe 提问时间:2/5/2015 最后编辑:Violet Giraffe 更新时间:2/7/2015 访问量:155
在此类的声明中声明类的实例并就地初始化它
Declaring an instance of a class inside this class' declaration and initializing it in-place
问:
这就是我现在所拥有的:
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);
};
但它会产生一个编译错误:
具有类内初始值设定项的静态数据成员必须具有非易失性常量整型
有没有办法避免在这里需要单独的声明和定义?
答:
这里的经验法则是,如果成员变量是(而不是),则不能使用成员变量的类内成员初始化,但是有一些例外(只是没有适用于您的情况)。static
const 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):
- 有一个“微不足道”的析构函数
- 只有常量表达式构造函数
- 仅具有文本类型基类和数据成员
- 或者是聚合类型
- 或者是 、标量(例如,)、引用或文本类型的数组
void
int
如果出现以下情况,析构函数是“微不足道的”:
- 它是编译器生成的(即你没有定义一个)
- 每个非静态成员对象都有一个微不足道的析构函数
考虑到所有这些,你可能会看一下你的代码,然后想“嗯,好吧,我可以把我所有的构造函数都变成,然后改成,我很好。constexpr
static const CColorf blue
static constexpr CColorf blue
但是,在声明静态时,您的类是“不完整的”。让我们考虑以下示例:
class A{
private:
A member;
}
now 的每个实例都有一个 的实例。编译器为多少字节分配?它说不出来。无限多,也许是由于递归。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
所以你不走运。
评论
constexpr
constexpr
你不能这么做。
错误消息意味着您正在编译为 C++03,其中只能在其声明中初始化整数类型的常量静态成员;因此,您不能对任何类类型执行此操作。
C++11放宽了规则,但仍有限制:
- 键入的值必须为文本。你可以通过制作构造函数来使这个类型成为文字;但
constexpr
- 类型必须是完整的,并且类在其定义中不完整(成员定义中除外)
- 成员不得使用 ODR;也就是说,您只能将其用作右值表达式,而不能获取其地址或创建对它的引用。
虽然第一点是可以固定的,第三点只会限制你可以对成员做什么,而不是你是否可以定义它,但第二点使它变得不可能。您必须以通常的方式定义变量,在单个翻译单元的类之外。
如果要将所有内容保留在类定义中,并且希望将值用于帮助编译时优化,则可以定义一个函数而不是变量
static CColorf blue() {return CColorf(0.0f, 0.0f, 1.0f);}
评论
constexpr
CColof
constexpr
constexpr
constexpr
constexpr