具有 const static 非整数数据初始化、命名空间与结构的编译器行为
Compiler behavior with const static non integral data initialization, namespace vs struct
假设我有相同的两个不同版本 header 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,我可以看出在 class 中只允许初始化静态常量整数或枚举类型的意义,因为我猜 C++ 标准没有指定如何浮点应该在编译时实现,并将其留给处理器。但是在这一点上,命名空间的情况误导了我...
最后,问题:
- 在上述两种情况下,编译器如何将源代码转换为 object 代码?
- 为什么只有 header 的第二个版本会报错?
感谢您的帮助!
我认为您可以在以下讨论中找到一些有用的信息(可能不是您问题的完整答案..):Why can't I have a non-integral static const member in a class?
此外,我认为这个命名空间滥用的小例子可以帮助我们阐明您的第一个问题(至少对于命名空间的情况):
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 个翻译单元,否则只有一个。如您所见,两种情况下代码的不同行为与此示例中的每个翻译单元都拥有静态变量的独立副本这一事实是兼容的。这当然只是一个微不足道的例子......
假设我有相同的两个不同版本 header 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,我可以看出在 class 中只允许初始化静态常量整数或枚举类型的意义,因为我猜 C++ 标准没有指定如何浮点应该在编译时实现,并将其留给处理器。但是在这一点上,命名空间的情况误导了我...
最后,问题:
- 在上述两种情况下,编译器如何将源代码转换为 object 代码?
- 为什么只有 header 的第二个版本会报错?
感谢您的帮助!
我认为您可以在以下讨论中找到一些有用的信息(可能不是您问题的完整答案..):Why can't I have a non-integral static const member in a class?
此外,我认为这个命名空间滥用的小例子可以帮助我们阐明您的第一个问题(至少对于命名空间的情况):
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 个翻译单元,否则只有一个。如您所见,两种情况下代码的不同行为与此示例中的每个翻译单元都拥有静态变量的独立副本这一事实是兼容的。这当然只是一个微不足道的例子......