2008-12-16 84 views
65

我注意到C++不會編譯以下內容:爲什麼我不能在類中使用非整型靜態const成員?

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

然而,將愉快地允許一個變型,其中雙改變爲整型,無符號,或任何整數類型:

我的解決辦法是改變它讀取:

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

和身影,編譯器會聰明地內嵌在必要......但它給我留下CUR欠條。

爲什麼C++設計器允許我靜態const int或unsigned,但不是double?

編輯:我在Windows XP上使用visual studio 7.1(.net 2003)。

EDIT2:

問題已經回答了,但完成,錯誤我看到:

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

什麼編譯器/平臺,或者你看到它的倍數? – warren 2008-12-16 02:04:11

+0

你在VS7.1中收到什麼錯誤信息? – 2008-12-16 02:10:13

回答

43

問題是一個整數,編譯器通常不必不斷創建一個內存地址常數。它在運行時不存在,並且每次使用它都會內聯到周圍的代碼中。它仍然可以決定給它一個內存位置 - 如果它的地址曾經被使用過(或者如果它被const引用傳遞給一個函數),它必須。爲了給它一個地址,它需要在一些翻譯單元中定義。在這種情況下,您需要將聲明從定義中分離出來,否則它將在多個翻譯單元中定義。

使用g ++沒有優化(-O0),它會自動內聯常量整數變量,但不是常量double值。在更高的優化級別(例如-O1)中,它會將常量加倍。因此,下面的代碼編譯在-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 

對於最大的可移植性,你應該在頭文件中聲明的常量和在一些源文件中定義一次。沒有優化,這不會損害性能,因爲你不是優化反正,但是啓用優化,這可能會損害性能,因爲編譯器不能再將這些常量內聯到其他源文件中,除非啓用「整個程序優化」 。

+8

static const double d = 1.0;是無效的C++。它根本不應該編譯。該表單只適用於整型。這就是numeric_limits 中的min和max是函數而不是靜態常量的原因。我不確定它爲什麼編譯你。 – 2008-12-16 02:27:41

+3

它似乎是一個gcc擴展。與-pedantic產量編譯:「Foo.cpp中:4:錯誤:ISO C++禁止非整體式的成員常量‘d’的初始化‘常量雙’」 – 2008-12-16 02:29:31

3

我不知道爲什麼它會治療的雙重不同於一個int。我以爲我曾經使用過這種形式。這裏有一個替代的解決方法:

class Now_Better 
{ 
    static double const d; 
}; 

而在你的.cpp文件:

double const Now_Better::d = 1.0; 
+0

是的,我偏離了這種可能的解決方案,只是因爲我認爲我的閱讀起來比較容易。我不喜歡*有*在單獨的文件中聲明和初始化一個值(我的類在.h中)。謝謝。 – 2008-12-16 02:04:30

15

我看不出有什麼技術原因

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

Traditionally, evaluation of floating-point constant expression at compile-time is a thorny issue. For uniformity and generality, we suggest to allow constant-expression data of floating point types, initialized with any floating-point constant expressions. That will also increase compatibility with C99 [ISO99, §6.6] which allows

[#5] An expression that evaluates to a constant is required in several contexts. If a floating expression is evaluated in the translation envi- ronment, the arithmetic precision and range shall be at least as great as if the expression were being evaluated in the execution environ- ment.

7

它並沒有真正放棄的理由,但這裏是斯特勞斯在「C++編程語言第三版」說這個:

10.4.6.2 Member Constants

It is also possible to initialize a static integral constant member by adding a constant-expression initializer to its member declaration. For example:

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 
    // ... 
}; 

However, an initialized member must still be (uniquely) defined somewhere, and the initializer may not be repeated:

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

I consider this a misfeature. When you need a symbolic constant within a class declaration, use an enumerator (4.8, 14.4.6, 15.3). For example:

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

In that way, no member definition is needed elsewhere, and you are not tempted to declare variables, floating-point numbers, etc.

並在附錄C(技術性)在第C.5(常量表達式),斯特勞斯有這樣說「常數表達式」:

In places such as array bounds (5.2), case labels (6.3.2), and initializers for enumerators (4.8), C++ requires a constant expression. A constant expression evaluates to an integral or enumeration constant. Such an expression is composed of literals (4.3.1, 4.4.1, 4.5.1), enumerators (4.8), and consts initialized by constant expressions. In a template, an integer template parameter can also be used (C.13.3). Floating literals (4.5.1) can be used only if explicitly converted to an integral type. Functions, class objects, pointers, and references can be used as operands to the sizeof operator (6.2) only.

Intuitively, constant expressions are simple expressions that can be evaluated by the compiler before the program is linked (9.1) and starts to run.

注意,他幾乎離開了浮點爲能夠發揮'常量表達式'。我懷疑這些類型的常量表達式中沒有提供浮點數,僅僅因爲它們不夠「簡單」。

-1

這裏是根據我的理解上Stroustrup的說法有關在類定義

A class is typically declared in a header file and a header file is typically included into many translation units. However, to avoid complicated linker rules, C++ requires that every object has a unique definition. That rule would be broken if C++ allowed in-class definition of entities that needed to be stored in memory as objects.

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

所以基本上,這是不允許的,因爲C++不允許這樣。爲了使鏈接器規則更簡單,C++要求每個對象都有唯一的定義。

靜態成員在類作用域中只有一個實例,不像C中常用的常規靜態變量,在一個轉換單元中只有一個instatnce。

如果靜態成員是在類中定義的,並且類定義將被包括到許多翻譯單元中,以便鏈接器必須做更多的工作來決定哪個靜態成員應該被用作唯一一個通過所有相關的翻譯單元。

但是對於普通的靜態變量,它們只能在一個翻譯單元內使用,即使在不同的翻譯單元中具有相同名稱的不同靜態變量的情況下,它們也不會相互影響。鏈接器可以做簡單的工作來鏈接一個翻譯單元內的常規靜態變量。

爲了減少複雜性並給出基函數,C++爲整數或枚舉類型的靜態常量提供了唯一的類內定義。

相關問題