2016-02-28 74 views
1

讓編譯器的行爲說,我有兩個不同的版本相同的標題foo.hpp的,第一個:與常量靜態非整體數據初始化,命名空間VS結構

// File foo.hpp 
#ifndef FILE_FOO 
#define FILE_FOO 
namespace X 
{ 
    static const int i = 13; 
    static const double d = 17.0; 
} 
#endif 

,第二個:

// File foo.hpp 
#ifndef FILE_FOO 
#define FILE_FOO 
struct X 
{ 
    static const int i = 13; 
    static const double d = 17.0; 
}; 
#endif 

在後一種情況下,結構的使用是毫無意義的,但我的目的是突出我的問題。在這兩種情況下,我試圖建立以下源文件foo.cpp

// File foo.cpp 
#include "foo.hpp" 
#include <iostream> 
int main() 
{ 
    std::cout << X::i << std::endl; 
    std::cout << X::d << std::endl; 
    return 0; 
} 

但只有後者,我得到了以下錯誤:

In file included from foo.cpp:2: 
foo.hpp:7: error: floating-point literal cannot appear in a constant-expression 
foo.hpp:7: error: ISO C++ forbids initialization of member constant ‘d’ of non-integral type ‘const double’ 

我使用g++ 4.2.1(所以仍然有C++ 98標準)和-pedantic,這個選項是嚴格要求得到上述錯誤。

正如我們討論的here,我可以看到只允許在類內部初始化靜態常量積分或枚舉類型,因爲我猜C++標準沒有指定在編譯時應如何實現浮點,並且它離開它到了處理器。但此時的命名空間的情況下是在誤導我...

最後,問題

  • 如何編譯器的行爲,並轉換源到目標代碼在上述兩種情況?
  • 它爲什麼只給頭的第二個版本一個錯誤?

感謝您的幫助!

回答

1

而且,我認爲,命名濫用這個小例子可以幫助我們擺脫

StaticNamespace.hpp:

#pragma once 
namespace StaticNamespace 
{ 
    static double d = 1.0; 
} 

Class.hpp:

關於第一個問題(至少在命名空間的情況下),一些光
#include "StaticNamespace.hpp" 
#include <iostream> 

class Class 
{ 

public: 

#if 1 

    Class(); 

    void printFromSource() const; 

#else 

    Class(){ 
     StaticNamespace::d = StaticNamespace::d + 0.1; 
    } 

#endif 

    void printFromHeader() const { std::cout<<"Class from header "<<StaticNamespace::d<<" "<<&StaticNamespace::d<<std::endl; } 

    static void printDouble() { std::cout<<"Class static "<<StaticNamespace::d<<" "<<&StaticNamespace::d<<std::endl; } 

}; 

Class.cpp:

#include "Class.hpp" 

Class::Class() 
{ 
    StaticNamespace::d = StaticNamespace::d + 0.1; 
} 

void Class::printFromSource() const 
{ 
    std::cout<<"Class from source "<<StaticNamespace::d<<" "<<&StaticNamespace::d<<std::endl; 
} 

main.cpp中:

#include <iostream> 
#include "Class.hpp" 

int main() 
{ 
    Class test_class; 

    test_class.printFromHeader(); 

#if 1 
    test_class.printFromSource(); 
#endif 

    Class::printDouble(); 
} 

如果您設置預處理器IFS爲true,你將有2個翻譯單元,否則會出現只有一個。正如你所看到的,在這兩種情況下代碼的不同行爲與這個例子中的每個翻譯單元都擁有一個獨立的靜態變量副本是一致的。這當然只是一個微不足道的例子......

+0

看到這種對翻譯單元的依賴性真的很有趣。只需一個單獨的翻譯單元,我就能得到我期望的確切輸出,例如:1.1。使用多個翻譯單元'printFromHeader'和靜態方法'printDouble'輸出** 1 **,而'printFromSource'則給出** 1.1 **。 作爲對我們所看到的附加確認,可以在cout中添加'&StaticNamespace :: d',我們將看到**不同的內存地址** –

+0

上述行爲也適用於英特爾編譯器 –

+0

連同鏈接到[棄用靜態關鍵字...沒有更多?](http://stackoverflow.com/questions/4726570/deprecation-of-the-static-keyword-no-more)你在另一個答案中提供,我認爲我們可以關閉這個問題 –

1

我認爲你可以找到一些有用的信息(也許不是一個完整的回答你的問題。)在下面的討論:Why can't I have a non-integral static const member in a class?

+0

謝謝克里斯託弗,我已經看過那個線程,但我可以說它有點不同,因爲只有一個主源文件並且沒有頭文件。在這種情況下,你將不會得到任何錯誤,也不會在'-O0'和'-O1'之間出現任何差異,至少在'GCC 4.2.1'我已經嘗試過... –

+1

關於你的第二個問題,我認爲你可以找到有趣的討論:[鏈接](http://stackoverflow.com/questions/4726570/deprecation-of-the-static-keyword-no-more) 基本上你不應該能夠做你在做什麼基於命名空間的情況... – Christopher23

+0

好的,這回答了第二個問題。謝謝 –