初始化C / C ++中的结构的const成员...依赖于编译器?

时间:2011-07-15 06:06:01

标签: c++

最近,我在使用Borland C ++ 5.2的旧环境中遇到了编译器错误。我有一个.cpp文件,其中包含一些我无法控制的C源头。头部包含一个包含const成员的结构定义,并且编译器抱怨“没有构造函数的类中的常量成员”。经过调查,此错误似乎与编译器有关。以下是各种编译器的结果示例代码:

#include <stdio.h>

typedef struct {
   const float a;
} _floater;

int main()
{
   _floater f = {5.1F};

   printf("%f\r\n",f.a);

   return 0;
}

Borland 5.2

E:\Projects\Scratchpad>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:\Projects\Scratchpad>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:\Projects\Scratchpad>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:\Projects\Scratchpad>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没有声明,但是当你尝试用初始化列表实例化它时会抱怨 - 考虑结构非聚合类型。 VS2008和g ++非常高兴。 [道歉..我刚刚意识到错误中的行#s是错误的,因为我在发布前删除了一些注释掉的行。]

Microsoft对聚合的定义如下: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. 有任何变通方法吗?鉴于我遇到了编译器版本和头文件,我没有看到任何。
  3. 谢谢!

4 个答案:

答案 0 :(得分:7)

标准很清楚。拥有const成员不会禁止某个类成为聚合。

8.5.1 [dcl.init.aggr]

  

聚合是一个数组或类(第9节),没有用户声明的构造函数(12.1),没有私有或受保护的非静态数据成员(第11节),没有基类(第10条),没有虚函数(10.3)。

copy-intialize const对象是合法的,这是聚合初始化对聚合成员执行的初始化。在12.6.2的 mem-initializer-list 中没有命名没有用户声明的构造函数的const对象的限制仅适用于由于聚合而不适用的构造函数的初始化初始化发生了。

至于为什么旧版编译器出现故障,我不知道。我只能说他们在这方面不符合标准。

答案 1 :(得分:2)

问题1:

在不同时间发布的编译器实现了不同版本的C ++。它们是标准的不同近似值。所有都有特定于供应商的“添加”,即允许不可移植的代码。

  • BCC 5.2是在第一个C ++标准(ISO 14882:1998)之前创建的。
  • VC ++ 2003接近ISO 14882:1998,但也有一些不足之处,特别是有关模板的问题。
  • VC ++ 2008几乎实现了ISO 14882:2003。

问题2:

  • 如果是32位Windows,请尝试将现代编译器添加到旧系统中。
  • 尝试在现代计算机上编译并在旧系统上部署可执行文件。
  • 如果旧系统是16位Windows,我看不到解决方案。

答案 2 :(得分:1)

在同一台Linux机器上的g ++ 4.6.1上,-Wall -ansi -pedantic没有出现任何警告。

这一点在编译器编写者的议程中可能并不是那么高。它看起来对我来说,看看VS2003和VS2008的行为,看看你发布的g ++ 3.3.3的行为以及我观察到的g ++ 4.6.1的行为。

您能考虑将const更改为private:,非常量吗?这样,你可以通过不为它导出setter来控制谁写入它,而不会产生编译器错误。

答案 3 :(得分:1)

只是为MSVC和G ++添加一些关于这个主题的额外信息,并正好回应@Charles Bailey和@RenéRichter说的话;它是可接受的C ++ 03和C ++ 11但是根据编译器的年龄和实现状态,如果有的话,你会得到不同的错误。

实际上我尝试了Dinkum's online test compilers用于EDG,MSVC2008和G ++,并且都编译了你给出的样本,但不是我给出的样本。

所以简而言之完全初始化的 结构使用const成员将在MSVC2008和G ++ 4.5上成功编译,但是经过测试的编译器 none可以编译 “部分初始化”(或部分聚合初始化) POD结构即使在C ++标准中也是允许的 - 我甚至联系了一些G ++错误维护者以确保我正确地阅读了该标准并且他们确认它甚至可以在当前的C ++ 03编译器中工作。

您可以在GNU BugzillaMicrosoft's Visual Studio help pages上查看相关的错误,这些错误实际上是与此other stackoverflow article titles "why do I get this warnings in Visual Studio when building a struct?相关联的,Microsoft's Error C3852 as a known behavior of even MSVC2010也与{{3}}相关

// 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: %i\n", c ) ;              // should be an undef-value
  printf( "c2: %i\n", 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 %i\n", 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=%i\n", a0.m ) ; // a0 should not exist due compile errors
  printf( "a1.m=%i\n", a1.m ) ; // a1.m should be 2
  printf( "a2.m=%i\n", a2.m ) ; // a2.m should be 0
  printf( "a3.m=%i\n", 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( "%i\n", bee1.bbb ) ;  
}