在 C + + 中的类声明中初始化 const 成员

在 PHP 和 C # 中,常量可以在声明时初始化:

class Calendar3
{
const int value1 = 12;
const double value2 = 0.001;
}

下面是一个函数的 C + + 声明,它与另一个类一起用来比较两个数学向量:

struct equal_vec
{
bool operator() (const Vector3D& a, const Vector3D& b) const
{
Vector3D dist = b - a;
return ( dist.length2() <= tolerance );
}


static const float tolerance = 0.001;
};

这段代码的编译没有遇到 g + + 的问题,现在在 C + + 0x 模式(- std = c + + 0x)下,g + + 编译器输出一个错误消息:

错误: 类内初始化非整型静态数据成员“公差”所需的“ conconexpr”

我知道我可以在类定义之外定义和初始化这个 static const成员。此外,可以在构造函数的初始化器列表中初始化非静态常数数据成员。

但是有没有什么方法可以像在 PHP 或 C # 中那样在类声明中初始化一个常量呢?

更新

我使用 static关键字仅仅是因为可以在 g + + 中的类声明中初始化这些常量。我只需要一种方法来初始化类声明中的常量,不管它是否声明为 static

97574 次浏览

Yes. Just add the constexpr keyword as the error says.

Initializing static member variables other than const int types is not standard C++ prior C++11. The gcc compiler will not warn you about this (and produce useful code nonetheless) unless you specify the -pedantic option. You then should get an error similiar to:

const.cpp:3:36: error: floating-point literal cannot appear in a constant-expression
const.cpp:3:36: warning: ISO C++ forbids initialization of member constant ‘tolerance’ of non-integral type ‘const float’ [-pedantic]

The reason for this is that the C++ standard does not specifiy how floating point should be implemented and is left to the processor. To get around this and other limitations constexpr was introduced.

In C++11, non-static data members, static constexpr data members, and static const data members of integral or enumeration type may be initialized in the class declaration. e.g.

struct X {
int i=5;
const float f=3.12f;
static const int j=42;
static constexpr float g=9.5f;
};

In this case, the i member of all instances of class X is initialized to 5 by the compiler-generated constructor, and the f member is initialized to 3.12. The static const data member j is initialized to 42, and the static constexpr data member g is initialized to X0.

Since float and double are not of integral or enumeration type, such members must either be constexpr, or non-static in order for the initializer in the class definition to be permitted.

Prior to C++11, only static const data members of integral or enumeration type could have initializers in the class definition.

If you only need it in the one method you can declare it locally static:

struct equal_vec
{
bool operator() (const Vector3D& a, const Vector3D& b) const
{
static const float tolerance = 0.001f;
Vector3D dist = b - a;
return ( dist.length2() <= tolerance );
}
};

I ran into real problems with this, because I need the same code to compile with differing versions of g++ (the GNU C++ compiler). So I had to use a macro to see which version of the compiler was being used, and then act accordingly, like so

#if __GNUC__ > 5
#define GNU_CONST_STATIC_FLOAT_DECLARATION constexpr
#else
#define GNU_CONST_STATIC_FLOAT_DECLARATION const
#endif


GNU_CONST_STATIC_FLOAT_DECLARATION static double yugeNum=5.0;

This will use 'const' for everything before g++ version 6.0.0 and then use 'constexpr' for g++ version 6.0.0 and above. That's a guess at the version where the change takes place, because frankly I didn't notice this until g++ version 6.2.1. To do it right you may have to look at the minor version and patch number of g++, so see

https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html

for the details on the available macros.

With gnu, you could also stick with using 'const' everywhere and then compile with the -fpermissive flag, but that gives warnings and I like my stuff to compile cleanly.

Not great, because it's specific to gnu compilers, butI suspect you could do similar with other compilers.