实施提供的副本构造函数和作业操作员
-
30-09-2019 - |
题
我对实施(编译器)不会提供复制构造函数和复制分配运营商的情况有很小的困惑。
- 当我们在我们的课程中声明副本CTOR和/或复制分配运算符时。
- 有人说,当我们从具有私有副本CTOR和/或复制分配运算符的课程中得出时。
我对第二种情况有些困惑,第二种情况正是第二种情况。
a)实现不会为您声明它们,因此您将获得编译时间错误。
或者
b)实现将声明并定义它们,但是当编译器定义的实现试图找到基类方法时,我们将获得编译时间错误。
我昨天接受了一次采访,我说正在发生的(b)正在发生,但是面试官不同意,他说(a)。
我尝试在Microsoft C/C ++ 14.00和GCC 4.4.5中编译以下代码。
struct A
{
private:
A& operator = ( const A& );
};
struct B : A
{
};
int main()
{
B b1;
B b2;
b1 = b2;
return 0;
}
Microsoft编译器输出
ctor01.cpp(9) : error C2248: 'A::operator =' : cannot access private member declared in class 'A'
ctor01.cpp(4) : see declaration of 'A::operator ='
ctor01.cpp(2) : see declaration of 'A'
This diagnostic occurred in the compiler generated function 'B &B::operator =(const B &)'
GCC编译器输出
Ctor01.cpp: In member function ‘B& B::operator=(const B&)’:
Ctor01.cpp:4: error: ‘A& A::operator=(const A&)’ is private
Ctor01.cpp:8: error: within this context
Ctor01.cpp: In function ‘int main()’:
Ctor01.cpp:15: note: synthesized method ‘B& B::operator=(const B&)’ first required here
因此,我认为实现将声明并定义它,但是当编译器定义的实现试图找到基类方法时,我们将获得编译时间错误。如果我错了,请纠正我。
解决方案
关于复制构造函数,这是标准所说的(12.8/7):
如果复制构造函数的类 隐式定义 拥有:
- 具有不可访问或模棱两可的复制构造函数类类型(或其数组)的非静态数据成员,或
- 具有无法访问或模棱两可的复制构造函数的基类.
关于副本分配运营商(12.8/12):
如果副本分配运营商所在的类 隐式定义 拥有:
- const类型的非静态数据成员,或
- 参考类型的非静态数据成员,或
- 具有不可访问的副本分配操作员的类类型(或其数组)的非静态数据成员,或
- 具有无法访问的副本分配运算符的基类。
编译器如何报告错误或实际方式 下降 从我的角度来看,它几乎是无关紧要的。
但是,我确实相信答案(b)可能是 更多的 正确:声明了基类副本分配,并且无法访问。派生的班级有一个隐含的 宣布 复制编译器将尝试的作业 定义 如果使用, ,从而使该程序形成不佳。
其他提示
一堂课将隐含地具有复制构造函数和复制分配运算符 宣布 如果没有用户声明的版本。这总是发生。
简单地说,实施将隐含 定义 仅当它们实际使用时。如果,如果实施实现它们定义它们,则隐式定义将不正确(例如,用于复制分配,类包含参考成员或const成员,或者对于复制构造函数,基础或成员具有私有复制构造函数),则该程序是不构建的。
如果程序包含隐含的类,仍然可以有效 宣布 复制构造函数和复制分配运算符,只要它不会通过使用或导致使用它们来实际定义它们,就无法隐式定义。
您的情况(b)更准确。
C ++ 03标准12.8p10
如果类定义未明确声明副本分配运算符,则声明一个 隐式.
和12.8p12
隐式授权的副本分配运营商是 隐式定义 当将其类类型的对象分配给其类类型的值或从其类类型派生的类类型的值时。如果隐式分配运算符的类别具有:
- 一个非静态数据成员
const
类型,或 - 参考类型的非静态数据成员,或
- 具有不可访问的副本分配操作员的类类型(或其数组)的非静态数据成员,或
- 具有无法访问的副本分配运算符的基类。
隐式定义的复制构造函数,默认构造函数和破坏者的相应要求具有相似的措辞。
指定这些方法的存在,即使其定义将是非法的,可以阐明有关分辨率的一些事情。例如,
class A {
private:
A& operator=(const A&);
};
class B : public A {
public:
operator int() const;
B& operator=(int);
};
void f(B& b1, const B& b2)
{ b1 = b2; }
是非法的,因为隐含 B::operator=(const B&)
是更好的过载,但隐含的定义是错误的。没有该声明,您可能会认为编译器应隐式转换 b2
到 int
然后将其分配给 b1
.
我认为两者之间的区别取决于您特定实现的细节(并且没有太大的不同)。对于它的价值,Comeau给出了这一点:
"ComeauTest.c", line 7: error: "A &A::operator=(const A &)" (declared at line 4) is
inaccessible
struct B : A
^
detected during implicit generation of "B &B::operator=(const B &)"
at line 16
1 error detected in the compilation of "ComeauTest.c".
因此,在该编译器上,它检测到B分配运算符的隐式生成期间的错误。换句话说,它试图生成它,并发现它不能。它是在写出它还是通过查看它来检测到它 A
直接,并不重要。
这就是发生的事情:
struct A
{
private:
A& operator = ( const A& );
};
struct B : A
{
B& operator = ( const B& other )
{
A::operator=( other );
return *this;
}
};
int main()
{
B b1;
B b2;
b1 = b2;
return 0;
}
默认操作员=试图调用:: operator =是私有的。
该标准似乎同意您的看法。引用当前草案:
§12.8/8:
如果类定义没有明确声明复制构造函数,并且没有用户宣布的移动构造函数,则隐式构造函数被隐式声明为违约(8.4)。
§12.8/12:
如果x具有:[…
- 无法复制/移动的直接或虚拟基类B,因为应用于B的相应构造函数的过载分辨率(13.3)会导致歧义性或从默认构造函数中删除或无法访问的函数[…
因此,声明和定义了合成的复制构造函数,但被定义为已删除。