2016-02-28 31 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

好的,这回答了第二个问题。谢谢 –