为啥来自 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 T1
与cv2 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&
函数参数。所以我认为 Jerry 是对的:这是一个错误。
我改变了主意——重读标准后,我很确定这不是错误。以上是关于为啥来自 IBM XL C/C++ 编译器的警告?的主要内容,如果未能解决你的问题,请参考以下文章
IBM XL C/C++ 等效于#pragma GCC 优化
IBM XL C 编译器 - 我如何扩展用户包括但不包括系统包括
为啥我会收到来自 FxCop 的 InitializeReferenceTypeStaticFieldsInline 警告?