初始化C/ c++结构体的const成员…依赖于编译器的
Initializing const members of structs in C/C++... compiler dependent?
最近,我在使用Borland c++ 5.2的遗留环境中遇到了编译器错误。我有一个。cpp文件,其中包括一个头从一些我不控制的C源。头文件包含一个包含const成员的结构体定义,编译器会抱怨"类中没有构造函数的常量成员"。经过调查,这个错误似乎与编译器有关。下面是一些来自不同编译器的示例代码:
#include <stdio.h>
typedef struct {
const float a;
} _floater;
int main()
{
_floater f = {5.1F};
printf("%frn",f.a);
return 0;
}
宝蓝5.2
E:ProjectsScratchpad>bcc32 -P const_float.c
Borland C++ 5.2 for Win32 Copyright (c) 1993, 1997 Borland International
const_float.c:
Error const_float.c 13: Constant member ' ::a' in class without constructors
*** 1 errors in Compile ***
Microsoft VS 2003 .NET:
E:ProjectsScratchpad>cl /TP const_float.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86
Copyright (C) Microsoft Corp 1984-1998. All rights reserved.
const_float.c
const_float.c(19) : error C2552: 'f' : non-aggregates cannot be initialized with
initializer list
Microsoft VS 2008:
C:ProjectsScratchpad>cl /TP const_float.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.21022.08 for 80x86
Copyright (C) Microsoft Corporation. All rights reserved.
const_float.c
Microsoft (R) Incremental Linker Version 9.00.21022.08
Copyright (C) Microsoft Corporation. All rights reserved.
/out:const_float.exe
const_float.obj
C:ProjectsScratchpad>const_float.exe
5.100000
g++ 3.3.3
$ g++ const_float.c -o const_float.exe
const_float.c:25:2: warning: no newline at end of file
$ ./const_float.exe
5.100000
请注意,Borland在结构体声明时失败了,因为它有一个const成员,但没有构造函数,而VS 2003在声明时是ok的,但是当你试图用初始化列表实例化它时就会抱怨——考虑到结构体是非聚合类型。VS2008和g++是完美的。[道歉. .我刚刚意识到,错误中的行#s是错误的,因为我在发布之前剥离了一些注释掉的行。
微软对聚合的定义在这里:http://msdn.microsoft.com/en-us/library/0s6730bb.aspx。我不清楚const成员会使结构体非聚合,但也许在2003年他们做到了。
最新的Borland (Embarcadero)编译器似乎也将此视为警告而不是错误:http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/devwin32/wrnmembnocons_xml.html .
那么,我想有两个问题:
- 为什么编译器的差异?标准在这一点上是否模棱两可?
- 工作区吗?考虑到我被卡住了编译器版本和头文件,我没有看到任何。
谢谢!
标准很清楚。拥有const
成员并不妨碍类成为聚合。
8.5.1 [dcl.init.aggr]
聚合是一个数组或类(第9条),没有用户声明的构造函数(12.1),没有私有或受保护的非静态数据成员(第11条),没有基类(第10条),也没有虚函数(10.3)。
复制初始化 const
对象是合法的,这是聚合初始化对聚合成员执行的初始化。在12.6.2中的mem-initializer-list中不能命名没有用户声明的构造函数的const
对象的限制仅适用于构造函数的初始化,该构造函数不适用,因为会发生聚合初始化。
至于为什么旧的编译器会失败,我不知道。我只能说他们在这方面不符合标准。
问题1:
在不同时间发布的编译器实现了不同版本的c++。它们是同一标准的不同近似值。它们都有特定于供应商的"附加功能",即允许不可移植的代码。- BCC 5.2创建于第一个c++标准(ISO 14882:1998)之前。
- vc++ 2003接近ISO 14882:1998,但也有一些不足,特别是在模板方面。
- vc++ 2008几乎实现了ISO 14882:2003。
问题2:
- 如果你的旧系统是32位Windows,试着安装一个现代的编译器。 尝试在现代机器上编译,并在旧系统上部署可执行文件。
- 如果旧系统是16位Windows,我看不到解决方案。
在同一台Linux机器上的g++ 4.6.1上,-Wall -ansi -pedantic
没有任何警告。
这一点在编译器编写者的日程上可能不是那么重要。在我看来是这样,看看VS2003和VS2008的行为,看看你发布的g++ 3.3.3的行为和我观察到的g++ 4.6.1的行为。
你能考虑把const
改成private:
,非const吗?这样,您仍然可以通过不为其导出setter来控制谁向其写入,而不会生成编译器错误。
只是为了给MSVC和g++的主题添加一些额外的信息,并呼应@Charles Bailey和@ ren
实际上,我尝试了Dinkum的EDG, MSVC2008和g++的在线测试编译器,所有编译您给出的示例,但不是我下面给出的示例。
因此,简而言之,完全初始化的 结构与const成员将成功地在MSVC2008和g++ 4.5上编译,然而,经过测试的编译器没有一个可以编译 "部分初始化"(或部分聚合初始化) POD结构,即使这在c++标准中也是允许的-我甚至联系了一些g++的bug维护者,以确保我正确阅读标准,他们确认它应该工作,即使在当前的c++ 03编译器。
你可以在GNU Bugzilla和微软的Visual Studio帮助页面上看到相关的错误,该页面实际上是链接自另一篇stackoverflow文章"为什么我在Visual Studio中构建结构时得到这个警告?"这也与微软的错误C3852有关,甚至是MSVC2010的已知行为
// All sections refer to Draft C++03 (brackets refer to draft C++11)
//
// 3.9.3 CV (const/volatile) definition as "const data-type [= optional init]"
// 7.1.5.1/1 The cv-qualifiers [ 7.1.6.1/1 in C++11 ]
// "init-declarator-list of the declaration shall not be empty"
const int constval = 10 ;
// HOWEVER:
// 7.1.5.1/2 The cv-qualifiers [ 7.1.6.1 in C++11 ]
// [Note: as described in 8.5, the definition of an object or subobject
// of const-qualified type must specify an initializer or be subject to
// default-initialization. ]
// 8.5 Initializers
// 8.5/9 (C++11 8.5/11)
// Otherwise, if no initializer is specified for a non-static
// object, the object and its sub-objects, if any, have an indeterminate
// initial value(*90); if the object or any of its sub-objects are of
// const-qualified type, the program is ill-formed.
//
// *90: This does not apply to aggregate objects with automatic storage
// duration initialized with an incomplete brace-enclosed initializer list
// see 8.5.1.
// [ C++11 this sub-clause has been removed, however the list-initializer section
// pretty much covers the same topic - see 8.5.1/7 below ]
//
// 8.5.1 Aggregate definition
// 8.5.1/7 (C++11 8.5.1/7)
// If there are fewer initializers in the list than there are members in the
// aggregate, then each member not explicitly initialized shall be
// value-initialized (8.5).
//
// 8.5/5 value initialization
// if T is a class type (clause 9) with a user-declared constructor
// (12.1), then the default constructor for T is called (and the
// initialization is ill-formed if T has no accessible default constructor)
// ...
// otherwise, the object is zero-initialized
//
// 8.5/5 zero initialization
// if T is a scalar type (3.9), the object is set to the value of 0 (zero) converted to T;
//
// POD type
struct A {
int n ;
const int m ; // "const" causes failure in MSVC to make default constructor
} ;
// Example of non-POD
struct B {
int bbb ;
B(){}
} ;
#include <stdio.h>
int main() {
// C++03/11 ill formed code, fails as expected
const int c0 ; // per 7.1.5.1 "not allowed to be default init"
// OK
const int c = *new int ; // A default initialized constant
const int c2 = *new int(); // A zero-init, above is DEFAULT-INIT
printf( "c: %in", c ) ; // should be an undef-value
printf( "c2: %in", c2 ) ; // should be 0
// OK ; Array example making sure it works
const int aa[5] = {}; // all items value->zero-initialized per the above 8.5.1/7
printf( "aa: %i %i %in", aa[0], aa[2], aa[4] ) ;
// C++03/11 ill formed code, no initializer (G++/MSVC should fail)
A a0 ; // Correct error - no default constructor or initializer (8.5/9)
// C++03/11 correctly formed code, full initializer list (G++/MSVC should pass)
A a1 = {1,2}; // Explicit initialization OK, G++/MSVC pass
// C++03/11 correctly formed code; copy initialized from a value-initialized A()
A a2 = A(); // G++ OK, MSVC FAIL
// C++03/11 correctly formed code; aggregate partial intializer (8.5.1/7 agg list init)
A a3 = {}; // G++/MSVC FAIL
A a4{}; // C++11 only - doesnt work in G++ (didnt try MSVC2010)
printf( "a0.m=%in", a0.m ) ; // a0 should not exist due compile errors
printf( "a1.m=%in", a1.m ) ; // a1.m should be 2
printf( "a2.m=%in", a2.m ) ; // a2.m should be 0
printf( "a3.m=%in", a3.m ) ; // a3.m should be 0
// C++03/11 correctly formed code; user-default constructor supplied.
const B bee1 ; // Default constructor marks bbb as "initialized"
const B bee2 = {} ; // CORRECTLY flagged error; init of non-aggregate
printf( "%in", bee1.bbb ) ;
}
- 如何添加依赖于类本身的模板成员变量
- 初始化依赖于子类的继承类的常量类成员
- 类成员函数参数列表是否可以依赖于模板参数?
- 依赖于类成员属性的类实例成员
- 依赖于依赖类型的非静态数据成员的非限定名称
- 如何让成员函数实现依赖于类的模板参数?
- 具有依赖于实现的成员函数类型的多个静态接口
- 初始化依赖于另一个类成员的类成员
- 在非静态成员函数中使用时依赖于非静态成员的名称
- 否除非依赖于成员函数的否
- GCC 为依赖于默认构造函数的模板化类中的静态数据成员提供"undefined reference"错误
- 如何更好地处理依赖于模板参数的类成员类型
- 如何使用指向通用模板类的指针来访问依赖于模板参数类型的成员函数
- 如何专门化依赖于类的静态数据成员的模板?
- C++std列表排序使用自定义比较器,该比较器依赖于对象实例的成员变量
- 类中包含依赖于类成员的模板
- 如何初始化模板化类中依赖于T类型的静态const成员
- 测试一个方法的私有成员是否依赖于时钟时间
- 初始化C/ c++结构体的const成员…依赖于编译器的
- 枚举成员类型仍然依赖于实现