【问题标题】:Compiler behavior with const static non integral data initialization, namespace vs struct带有 const 静态非整数数据初始化、命名空间与结构的编译器行为
【发布时间】:2016-02-28 01:13:07
【问题描述】:

假设我有两个不同版本的同一个标头foo.hpp,第一个:

// 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++ 标准没有指定在编译时应该如何实现浮点,它把它留给处理器。但在这一点上,命名空间案例误导了我......

最后,问题

  • 在上述两种情况下,编译器如何将源代码翻译成目标代码?
  • 为什么只有第二个版本的标头才会报错?

感谢您的帮助!

【问题讨论】:

    标签: static compilation initialization g++ constants


    【解决方案1】:

    此外,我认为这个命名空间滥用的小例子可以帮助我们阐明您的第一个问题(至少对于命名空间的情况):

    静态命名空间.hpp:

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

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

    类.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 个翻译单元,否则只有一个。如您所见,这两种情况下代码的不同行为与此示例中的每个翻译单元都拥有静态变量的独立副本这一事实兼容。当然这只是一个简单的例子......

    【讨论】:

    • 看到这种对翻译单元的依赖真的很有趣。只需一个翻译单元,我就能得到我期望的准确输出,例如:1.1。使用多个翻译单元printFromHeader 和静态方法printDouble 输出1,而printFromSource 给出1.1。作为对我们所见内容的额外确认,可以将 &amp;StaticNamespace::d 添加到 couts 中,我们将看到不同的内存地址
    • 上述行为也适用于英特尔编译器
    • 连同你在另一个答案中提供的Deprecation of the static keyword… no more? 的链接,我想我们可以关闭这个问题
    【解决方案2】:

    我认为您可以在以下讨论中找到一些有用的信息(可能不是您问题的完整答案..):Why can't I have a non-integral static const member in a class?

    【讨论】:

    • 谢谢克里斯托弗,我看过那个帖子,但我可以说它有点不同,因为只有一个主源文件,没有标题。在这种情况下,您不会收到任何错误,也不会在-O0-O1 之间出现任何差异,至少对于我尝试过的GCC 4.2.1...
    • 关于你的第二个问题,我认为你会发现这个讨论很有趣:link 基本上你不应该能够在基于命名空间的情况下做你正在做的事情......
    • 好的,这就回答了第二个问题。谢谢
    猜你喜欢
    • 2013-09-22
    • 2011-05-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多