这是一个说明问题的最小代码示例:
#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并且没有诊断。为什么需要访问复制构造函数?除了使对象可复制(在我无法控制之外)或使显式副本通过(当真实对象的复制费用昂贵)时,我如何消除警告?
答案 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 T1
与cv2 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);