2015-02-05 162 views
5

这初始化它是我现在有:声明这个类声明中一个类的实例,并就地

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

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

a static data member with an in-class initializer must have non-volatile const integral type

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

+2

声明吧'constexpr'。 'CColof'是一种文字类型,因此应该可以工作。 – Columbo 2015-02-05 14:56:07

+0

@Columbo:不是'constexpr'应该用于表达式(或函数),而不是声明? – 2015-02-05 14:57:22

+1

...不知道你的意思,但你听起来很迷惑。 'constexpr'是一个decl-specifier(声明说明符),因此只能在声明中使用。 – Columbo 2015-02-05 14:59:02

回答

1

你不能那样做。

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

C++ 11松弛的规则,但仍存在限制:

  • 类型必须是字面。你可以通过使构造函数constexpr;但
  • 类型必须是完整,和一个类不是在其定义内完成(除了内侧部件定义)
  • 的构件必须不ODR使用的;也就是说,您只能使用它作为右值表达式,并且不能取其地址或创建对其的引用。

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

如果你想保留一切在类的定义,并可以帮助编译时优化的价值,你可以定义一个函数,而不是一个变量

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

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

在C++ 98标准中,只能构件初始化static const int

在C++ 11标准,可以构件初始化一切除static(有例外到C++ 98标准)。

,如果你能解决这个问题您的静态成员是constexpr

§9.4.2(2014年11月草案)

If a non-volatile const static data member is of integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression (5.20). A static data member of literal type can be declared in the class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression. [ Note: In both these cases, the member may appear in constant expressions. — end note ] The member shall still be defined in a namespace scope if it is odr-used (3.2) in the program and the namespace scope definition shall not contain an initializer.

要多一点清楚地解释这个片段:如果你想 尝试用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 bluestatic constexpr CColorf* blue = new CColorf(0.0f, 0.0f, 1.0f);

一个指针,但你不能,因为new经营者不得constexpr

而且你不能尝试const因为我们已经去了,为什么。

因此,也许你想重载new运营商是constexpr,但you can't do that either

所以你运气不好。

+0

请参阅Mike Seymour对该问题的评论。你所建议的不是一个答案。 – 2015-02-05 22:06:40

+0

我的问题的答案是要么显示如何做我想要的代码或3个字:“这是不可能的”。你的“答案”既不是;它说:“如果你的静态成员是'constexpr',你可以解决这个问题,但是让我的静态成员'constexpr'实际上并不能解决问题。更不用说标准中那些非人类可读的文本几乎告诉我任何东西。 – 2015-02-06 05:41:53

+0

@VioletGiraffe:我很抱歉。有时对一个人来说很清楚的事情可能对另一个人不那么清楚。我在答复中增加了更多细节,希望更清楚。 – AndyG 2015-02-06 14:04:27