初始化C/ c++结构体的const成员…依赖于编译器的

Initializing const members of structs in C/C++... compiler dependent?

本文关键字:成员 依赖于 编译器 const 初始化 c++ 结构体      更新时间:2023-10-16

最近,我在使用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 .

那么,我想有两个问题:

  1. 为什么编译器的差异?标准在这一点上是否模棱两可?
  2. 工作区吗?考虑到我被卡住了编译器版本和头文件,我没有看到任何。

谢谢!

标准很清楚。拥有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 Richter所说的;它是可以接受的c++ 03和c++ 11,但取决于你的编译器的年龄和实现状态,如果有的话,你会得到不同的错误。

实际上,我尝试了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 ) ;  
}