【问题标题】:Initializing const members of structs in C/C++... compiler dependent?在 C/C++ 中初始化结构的 const 成员...依赖于编译器?
【发布时间】:2011-07-15 06:06:01
【问题描述】:

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

微软 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 行是错误的,因为我在发布之前删除了一些注释掉的行。]

微软对聚合的定义在这里:http://msdn.microsoft.com/en-us/library/0s6730bb.aspx。对我来说,const 成员不会使 struct 非聚合,但也许他们早在 2003 年就做到了。

似乎最新的 Borland (Embarcadero) 编译器将此视为警告而不是错误:http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/devwin32/wrnmembnocons_xml.html

所以,我猜有 2 个问题:

  1. 为什么编译器会出现差异?标准在这一点上是否模棱两可?
  2. 有什么解决方法吗?鉴于我被编译器版本和头文件卡住了,我什么也没看到。

谢谢!

【问题讨论】:

  • 它与 gcc 4.6 可以正常编译,正如预期的那样。

标签: c++


【解决方案1】:

标准很明确。拥有const 成员并不禁止类成为聚合。

8.5.1 [dcl.init.aggr]

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

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

至于为什么旧的编译器会失败,我不知道。我只能说他们在这方面不符合标准。

【讨论】:

  • 谢谢,查尔斯。我担心这就是我所坚持的。
【解决方案2】:

问题一:

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

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

问题 2:

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

【讨论】:

  • 很好的附加信息,谢谢。不幸的是,我无法利用这些解决方法。
【解决方案3】:

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

这一点在编译器作者的议程上可能没有那么重要。在我看来,查看 VS2003 和 VS2008 的行为,查看您发布的 g++ 3.3.3 的行为和我观察到的 g++ 4.6.1 的行为。

您可以考虑将const 更改为private:,非 const 吗?这样,您仍然可以通过不为它导出 setter 来控制谁写入它,同时不产生编译器错误。

【讨论】:

  • 感谢您检查 4.6.1。我无法修改 .h 或更改编译器,所以我想我被卡住了——至少卡住了使用不同的头文件回到旧方法。
【解决方案4】:

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

实际上,我为 EDG、MSVC2008 和 G++ 尝试了 Dinkum's online test compilers,并且都编译了您提供的示例,但没有编译我在下面提供的示例。

简而言之,完全初始化 带有 const 成员的结构 将在 MSVC2008 和 G++4.5 上成功编译,但是在经过测试的编译器中没有一个可以编译 strong> “部分初始化”(或部分聚合初始化) 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 相关

// 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 ) ;  
}

【讨论】:

    猜你喜欢
    • 2021-04-13
    • 1970-01-01
    • 2021-02-10
    • 1970-01-01
    • 2020-12-17
    • 1970-01-01
    • 1970-01-01
    • 2011-05-11
    • 1970-01-01
    相关资源
    最近更新 更多