为什么IBM XL C / C ++编译器出现此警告?

时间:2009-10-23 20:15:55

标签: c++ compiler-warnings aix

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

#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并且没有诊断。为什么需要访问复制构造函数?除了使对象可复制(在我无法控制之外)或使显式副本通过(当真实对象的复制费用昂贵)时,我如何消除警告?

4 个答案:

答案 0 :(得分:9)

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

否则,使用非参考拷贝初始化的规则(8.5)从初始化表达式创建并初始化类型为“cv1 T1”的临时类型。然后将引用绑定到临时。如果T1与T2有参考关系,则cv1必须与cv-qualified相同,或者cv-qualification与cv2相同;否则,该计划是不正确的。

编辑:重读,我认为IBM做得对。我以前想过必须复制临时的可能性,但这不是问题的根源。要使用§8.5中指定的非引用副本初始化创建临时,它需要copy 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

答案 1 :(得分:3)

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

但为什么show(3)首先需要副本?我无法弄明白。幸运的是,litb会有所作为。

答案 2 :(得分:1)

我的直觉是杰瑞的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的更改仅影响rvalue临时具有相同类类型的位置。以前的措辞有:

  

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

     

[...]

     

无论副本是否真正完成,用于制作副本的构造函数都应该是可调用的。

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

  

如果初始化表达式是rvalue,T2是类类型,并且“cv1 T1”与“cv2 T2”引用兼容,则引用绑定到rvalue表示的对象(参见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)案例的解释错误,但是,由于不同编译器行为之间的差异,我确实有疑问。

答案 3 :(得分:0)

如果您尝试命名临时事件会发生什么?

Thing temp(3);
show(temp);