繁体   English   中英

为什么我不能在类中有一个非整数静态const成员?

[英]Why can't I have a non-integral static const member in a class?

我注意到C ++不会编译以下内容:

class No_Good {
  static double const d = 1.0;
};

但是,它很乐意允许将double更改为int,unsigned或任何整数类型的变体:

class Happy_Times {
  static unsigned const u = 1;
};

我的解决方案是将其更改为:

class Now_Good {
  static double d() { return 1.0; }
};

并认为编译器将足够聪明,可以在需要时进行内联...但是,这使我感到好奇。

为什么C ++设计人员可以让我将const静态地设置为int或unsigned,而不允许double?

编辑:我在Windows XP上使用Visual Studio 7.1(.net 2003)。

编辑2:

问题已经回答,但是为了完成,我看到的错误是:

error C2864: 'd' : only const static integral data members can be initialized inside a class or struct

问题在于,使用整数时,编译器通常不必为该常量创建内存地址。 它在运行时不存在,并且每次使用都会内联到周围的代码中。 它仍然可以决定给它一个内存位置-如果它的地址曾经被占用过(或者如果它被const引用传递给一个函数),则必须这样做。 为了给它一个地址,需要在某个翻译单元中对其进行定义。 在这种情况下,您需要将声明与定义分开,因为否则它将在多个翻译单元中进行定义。

使用无优化的g ++(- -O0 ),它将自动内联常量整数变量,而不是常量双精度值。 在更高的优化级别(例如-O1 )下,它内联常数double。 因此,以下代码在-O1处编译,但-O0处编译:

// File a.h
class X
{
 public:
  static const double d = 1.0;
};

void foo(void);

// File a.cc
#include <stdio.h>

#include "a.h"

int main(void)
{
  foo();
  printf("%g\n", X::d);

  return 0;
}

// File b.cc
#include <stdio.h>

#include "a.h"

void foo(void)
{
  printf("foo: %g\n", X::d);
}

命令行:

g++ a.cc b.cc -O0 -o a   # Linker error: ld: undefined symbols: X::d
g++ a.cc b.cc -O1 -o a   # Succeeds

为了获得最大的可移植性,您应该在头文件中声明常量,并在某些源文件中对其进行一次定义。 如果不进行优化,则不会影响性能,因为无论如何您都不会进行优化,但是启用优化会损害性能,因为编译器无法再将这些常量内联到其他源文件中,除非您启用“整个程序优化” 。

我看不出技术原因

struct type {
    static const double value = 3.14;
};

是禁止的。 您发现它可以工作的任何地方都是由于非便携式实现定义的功能。 它们似乎也只有有限的用途。 对于在类定义中初始化的整数常量,可以使用它们并将它们作为非类型参数传递给模板,并将它们用作数组尺寸的大小。 但是对于浮点常量,您不能这样做。 允许浮点模板参数会带来它自己的规则集,这确实不值得麻烦。

尽管如此,下一个C ++版本将允许使用constexpr

struct type {
    static constexpr double value = 3.14;
    static constexpr double value_as_function() { return 3.14; }
};

并将type::value设为常量表达式。 同时,最好的选择是遵循std::numeric_limits也使用的模式:

struct type {
    static double value() { return 3.14; }
};

它不会返回常量表达式(在编译时未知值),但这仅在理论上重要,因为实际情况下无论如何都将内联该值。 请参阅constexpr建议。 它包含

4.4

Floating-point constant expressions

传统上,在编译时评估浮点常量表达式是一个棘手的问题。 为了统一性和通用性,我们建议允许使用任何浮点常量表达式初始化的浮点类型的常量表达式数据。 这还将增强与C99 [ISO99,§6.6]的兼容性,从而允许

[#5]在多种情况下都需要一个计算结果为常量的表达式。 如果在翻译环境中评估浮动表达式,则算术精度和范围至少应与在执行环境中评估表达式一样大。

它并没有真正给出理由,但是,这是Stroustrup在“ C ++编程语言第三版”中对此要说的:

10.4.6.2成员常量

也可以通过在其成员声明中添加一个常量表达式初始值设定项来初始化静态整数常量成员。 例如:

 class Curious { static const int c1 = 7; // ok, but remember definition static int c2 = 11; // error: not const const int c3 = 13; // error: not static static const int c4 = f(17); // error: in-class initializer not constant static const float c5 = 7.0; // error: in-class not integral // ... }; 

但是,仍然必须(唯一地)在某个地方定义一个初始化成员,并且不得重复初始化该初始化器:

 const int Curious::c1; // necessary, but don't repeat initializer here 

我认为这是一个错误的功能。 当您在类声明中需要符号常量时,请使用枚举器(4.8、14.4.6、15.3)。 例如:

 class X { enum { c1 = 7, c2 = 11, c3 = 13, c4 = 17 }; // ... }; 

这样,在其他地方就不需要成员定义,也不会试图声明变量,浮点数等。

在第C.5节(常量表达式)的附录C(技术)中,Stroustrup所说的是“常量表达式”:

在诸如数组边界(5.2),大小写标签(6.3.2)和枚举器的初始化器(4.8)之类的地方,C ++需要一个常量表达式 常数表达式的计算结果为整数或枚举常数。 这样的表达式由文字(4.3.1、4.4.1、4.5.1),枚举器(4.8)和由常量表达式初始化的const组成 在模板中,也可以使用整数模板参数(C.13.3)。 仅当显式转换为整数类型时,才可以使用浮动文字(4.5.1)。 函数,类对象,指针和引用只能用作sizeof运算符(6.2)的操作数。

直观上,常量表达式是可以在程序链接(9.1)并开始运行之前由编译器求值的简单表达式。

请注意,他几乎可以将浮点数保留下来,因为它可以在“常量表达式”中使用。 我怀疑浮点数被排除在这些类型的常量表达式之外是因为它们不够“简单”。

我不知道为什么它将与int区别为两倍。 我以为我以前用过那种表格。 这是另一种解决方法:

class Now_Better
{
    static double const d;
};

在您的.cpp文件中:

double const Now_Better::d = 1.0;

这是我基于Stroustrup关于类定义的声明所得出的理解

通常在头文件中声明类,并且通常将头文件包含在许多翻译单元中。 但是,为避免复杂的链接器规则,C ++要求每个对象都有唯一的定义。 如果C ++允许对需要作为对象存储在内存中的实体进行类内定义,则该规则将被打破。

http://www.stroustrup.com/bs_faq2.html#in-class

所以基本上,这是不允许的,因为C ++不允许这样做。 为了使链接器规则更简单,C ++要求每个对象都有唯一的定义。

静态成员在类范围内只有一个实例,这与在C中大量使用的常规静态变量不同,后者在一个转换单元中只有一个实例。

如果在类中定义了静态成员,并且该类定义将包含在许多翻译单元中,则链接器必须做更多的工作来确定在所有相关的翻译单元中哪个静态成员应被用作唯一成员。

但是对于常规静态变量,它们只能在一个翻译单元中使用,即使在不同翻译单元中具有相同名称的静态变量的情况下,它们也不会相互影响。 链接器可以做简单的工作来将一个转换单元内的常规静态变量链接起来。

为了减少复杂性并提供基本功能,C ++为整数或枚举类型的静态const提供了唯一的类内定义。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM