为啥来自 IBM XL C/C++ 编译器的警告?

Posted

技术标签:

【中文标题】为啥来自 IBM XL C/C++ 编译器的警告?【英文标题】:Why this warning from IBM XL C/C++ compiler?为什么来自 IBM XL C/C++ 编译器的警告? 【发布时间】:2010-12-09 14:17:54 【问题描述】:

这是一个说明问题的最小代码示例:

#include <iostream>

class Thing

   // Non-copyable
   Thing(const Thing&);
   Thing& operator=(const Thing&);

   int n_;

public:
   Thing(int n) : n_(n) 

   int getValue() const  return n_;
;

void show(const Thing& t)

   std::cout << t.getValue() << std::endl;


int main()

   show(3);

这会产生同样的错误:

int main()

    show( Thing(3) );

AIX 下的 IBM XL C/C++ 8.0 编译器发出以下警告:

"testWarning.cpp", line 24.9: 1540-0306 (W) The "private" copy constructor "Thing(const Thing &)" cannot be accessed.
"testWarning.cpp", line 24.9: 1540-0308 (I) The semantics specify that a temporary object must be constructed.
"testWarning.cpp", line 24.9: 1540-0309 (I) The temporary is not constructed, but the copy constructor must be accessible.

我还使用“-Wall”和“-pedantic”尝试了 g++ 4.1.2,但没有得到诊断。为什么这里需要访问复制构造函数?除了使对象可复制(不在我的控制范围内)或使显式副本通过(当现实生活中的对象复制成本很高时)之外,我如何消除警告?

【问题讨论】:

您是否真的在某个地方的类实现中使用了该复制构造函数? 没有。我已经发布了生成诊断的整个代码文件。 仅供参考:我也尝试使用 VC++ 2005 和 2008,编译时没有警告。所以看起来你是对的,问题似乎是特定于 IBM 编译器的 【参考方案1】:

我的直觉是 Jerry 的answer 是正确的,但仍有一些问题。

有趣的是,该部分的前一段有一个核心问题(391)。该问题与参数是同一类类型时有关。具体来说:

int main () 
  show ( Thing (3) );       // not allowed under current wording
                            // but allowed with Core Issue 391

  show ( 3 );               // Still illegal with 391

核心问题391 中的更改仅影响右值临时具有相同类类型的位置。以前的措辞是:

如果初始化表达式是右值,T2 是类类型,并且cv1 T1cv2 T2, 引用兼容,则引用绑定如下:

[...]

无论复制是否实际完成,用于制作复制的构造函数都应该是可调用的。

根据当前标准,最后一行会使show(Thing(3)) 非法。本节的建议措辞是:

如果初始化表达式是一个右值,T2 是一个类类型,并且“cv1 T1”与“cv2 T2”是引用兼容的,则引用绑定到右值表示的对象(参见 3.10 [basic.lval ]) 或该对象内的子对象。

此时,我认为 g++ 可能已按照391 更新了其行为,但该更改意外地包含了复制初始化情况。但是,我测试的 g++ 版本并未证明这一点:

class A
public:
  A ();
  A (int);
private:
  A (A const &);
;

void foo (A const &);

void foo ()

  A a = 3 ;     // 3.2.3 (ERROR), 3.4.6(ERROR), 4.4.0(ERROR), Comeau(ERROR)
  
  foo ( 3 ) ;   // 3.2.3 (OK), 3.4.6(OK), 4.4.0(OK), Comeau(OK)
  foo ( A() );  // 3.2.3 (OK), 3.4.6(ERROR), 4.4.0(OK), Comeau(OK)
  foo ( A(3) ); // 3.2.3 (OK), 3.4.6(ERROR), 4.4.0(OK), Comeau(OK)

我在 Jerry 对 foo (3) 案例的解释中找不到错误,但是,由于不同编译器行为之间的差异,我确实有疑问。

【讨论】:

【参考方案2】:

这方面的规则在标准的 §8.5.3/5 中。确定了三种基本情况。第一个涉及初始化程序(在您的情况下为“3”)是左值或具有类类型。由于这些都不正确,因此您所拥有的是第三种情况:使用没有类类型的右值初始化 const 引用。 8.5.3/5 中的最后一个项目符号涵盖了这种情况:

否则,将使用非引用复制初始化 (8.5) 的规则从初始化表达式创建并初始化“cv1 T1”类型的临时变量。然后将引用绑定到临时文件。如果 T1 与 T2 引用相关,则 cv1 必须与 cv2 具有相同的 cv-qualification 或大于 cv2 的 cv-qualification;否则,程序格式错误。

编辑:重读,我认为 IBM 是正确的。我以前在考虑必须复制临时文件的可能性,但这不是问题的根源。要使用第 8.5 节中指定的非引用复制初始化创建临时,它需要复制 ctor。特别是,此时它相当于如下表达式:

T x = a;

这基本上相当于:

T x = T(a);

即需要创建一个临时对象,然后将该临时对象复制到正在初始化的对象(在这种情况下,也是一个临时对象)。总结一下所需的流程,大致相当于如下代码:

T temp1(3);
T temp2(temp1); // requires copy ctor
show(temp2);    // show's reference parameter binds directly to temp2

【讨论】:

好消息。感谢您的研究,杰瑞。 这真的很奇怪,但我同意您/IBM 对标准的解读。 需要创建一个临时的技术上:能够创建一个临时的。从技术上讲,编译器可以对此进行优化。 @Martin:嗯,是的,所有的要求都包含一个隐含的:“或等价的东西,只要它们真的等价到你无法用符合标准的代码区分的程度。” 你在这里说的很有道理。我唯一的问题是最近版本的 g++ 和 comeau 都拒绝“Thing t = 3”,但他们接受“show(3)”。这并不是说你说的是错的,但尤其是 Comeau 与行为不符让我感到奇怪....【参考方案3】:

如果您尝试命名临时事物会发生什么情况?Thing temp(3);show(temp);

【讨论】:

这消除了消息。我们可能会基本上这样做,但可能会使用堆分配。它没有那么快,但现实生活中的对象非常大,可以放入堆栈。所以也许最好还是不要依赖临时的。【参考方案4】:

C++ 允许足够智能的编译器避免复制临时对象,这是标准允许的 as-if 规则的一种违反。我不熟悉 IBM 的 AIX C++ 编译器,但听起来它认为 show(3) 调用需要复制临时事物。在这种情况下,C++ 要求您有一个可访问的复制构造函数,即使您的编译器足够聪明,可以避免使用它。

但是为什么show(3) 首先需要一个副本?我想不通。运气好的话,litb 很快就会出现。

【讨论】:

我就是这么想的。 "但是为什么 show(3) 首先需要一个副本?"我认为不应该。代码应该创建一个临时对象并将其直接绑定到const Thing&amp; 函数参数。所以我认为 Jerry 是对的:这是一个错误。 我改变了主意——重读标准后,我很确定这不是错误。

以上是关于为啥来自 IBM XL C/C++ 编译器的警告?的主要内容,如果未能解决你的问题,请参考以下文章

IBM XL C/C++ 等效于#pragma GCC 优化

使用 -Wall 编译 c++ - 来自其他文件的警告

IBM XL C 编译器 - 我如何扩展用户包括但不包括系统包括

来自链接器的奇怪警告(ld)[重复]

为啥我会收到来自 FxCop 的 InitializeReferenceTypeStaticFieldsInline 警告?

gcc和g++常用编译参数