存在引用时的赋值运算符和复制构造函数
我只是使用此代码尝试引用:
class A
{
};
class B
{
public:
B(A& a): m_a(a){}
A& m_a;
};
int main()
{
A a;
B b(a);
B b1 = b;
}
我期望 B b1 = b;
都会产生错误。相反,当我使用 VS2008 编译时,我只是收到警告
警告 C4512:“B”:赋值 无法生成运算符
我明白为什么收到此警告。但是编译器不应该也为 B b1 = b;
语句生成错误吗?就像它生成了复制构造函数但没有生成赋值运算符。这两者难道不是有着内在的联系吗?当无法生成其中一个的默认实现时,仅生成其中一个的默认实现是否有意义?
I am just experimenting with the references using this code:
class A
{
};
class B
{
public:
B(A& a): m_a(a){}
A& m_a;
};
int main()
{
A a;
B b(a);
B b1 = b;
}
I was expecting both B b1 = b;
to produce a error. Instead when I compile with VS2008 I just get a warning
warning C4512: 'B' : assignment
operator could not be generated
I understand why I am getting this warning. But shouldn't the compiler generating an error for the B b1 = b;
statement too? It is like it generated copy constructor but didn't generate assignment operator. Aren't these two inherently linked to one another ? does it makes sense to generate the default implementation for only one of them when the other could not be generated?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
问题 1:为什么会出现此警告?
引用只能在创建时初始化一次。创建后无法将引用重新分配给另一个相同类型的变量,因为引用只是为其创建的类型变量的别名,并将继续保持如此。尝试重新分配它会产生错误。
通常,编译器默认为每个类生成一个隐式按位赋值运算符,但在这种情况下,由于
class B
具有作为成员m_a
的引用,如果编译器要生成隐式赋值运算符会破坏引用不能重新分配的基本规则。因此编译器会生成此警告,通知您它无法生成隐式赋值运算符。问题 2:但是编译器不应该为 B b1 = b; 生成错误吗?也有声明吗?
生成的警告和此特定操作完全没有关系。
B b1 = b;
调用隐式(正如 @AndreyT 正确指出的)复制构造函数B::B(const B&)
。隐式复制构造函数是类默认生成的成员函数之一。所以没有警告或错误。问题3:好像生成了复制构造函数,但没有生成赋值运算符。这两者不是有内在的联系吗?
不,他们根本没有关系。是的,编译器生成了一个复制构造函数,但由于上面问题 1 的回答中指定的原因,它无法生成赋值运算符。这是因为成员引用 m_a 可以在构造函数本身的主体中初始化。它只是创建时的初始分配,而不是
=
情况下的分配。问题 4:当无法生成其中一个实现时,仅为其中一个生成默认实现是否有意义?
第三个问题的回答似乎回答了这个问题。
只是重申一下代码示例中正在执行的操作:
B b(a);
调用转换复制构造函数B:: B(A&)
B b1 = b;
调用默认复制构造函数B::B(const B&)
考虑其他场景。
如果您有
B b1 = a;
,它将调用B::B(A&)
,因此不会再出现错误。但是,如果
B::B(A&)
被声明为显式
,编译器会标记错误,并且不允许通过执行任何隐式转换
作为转换函数
。请在此处查看相同内容。
Question 1: Why this warning?
References can only be initialized once when they are created. You cannot reassign a reference to another same type variable after creation because Reference is just an alias of the type variable for which it was created and will continue to remain so. Attempting to reassign it generates an error.
Usually, a compiler generates an implicit bit wise assignment operator every class by default but in this case Since the
class B
has an reference as an memberm_a
, If the compiler were to generate an implicit assignment operator it would break the fundamental rule that references cannot be reassigned. So the compiler generates this warning to inform you that it could not generate the implicit assignment operator.Question 2: But shouldn't the compiler generating an error for the B b1 = b; statement too?
The generated warning and this particular operation have no relation at all.
B b1 = b;
invokes the implicit(as rightly pointed out by @AndreyT) copy constructorB::B(const B&)
. Implicit copy constructor is one of the member functions which a class generates by default. So there is no warning or error for it.Question 3: It is like it generated copy constructor but didn't generate assignment operator. Aren't these two inherently linked to one another ?
No they are not related at all. Yes compiler generated a copy constructor but it could not generate a assignment operator for the reason specified in answer to Question 1 above. This is because the member reference
m_a
could be initialized in the body of the constructor itself. it's just the initial assignment at time of creation not assignment as in case of=
.Question 4: Does it makes sense to generate the default implementation for only one of them when the other could not be generated?
Answer to the 3 Question seems to answer this.
Just to reiterate the operations being performed in your code example:
B b(a);
invokes the conversion copy constructorB::B(A&)
B b1 = b;
invokes the default copy constructorB::B(const B&)
Consider the additional scenarios.
If you had
B b1 = a;
it would callB::B(A&)
and hence no error again.But compiler would mark a error if
B::B(A&)
was declaredexplicit
and would not be allowed for anyimplicit conversions
by acting as aconversion function
.Check the same here.
C++ 语言中的构造函数执行初始化,而赋值运算符执行赋值。初始化和赋值是两个完全不同的概念。
C++语言中的引用是可以初始化的,这就是为什么编译器可以毫无问题地为带有引用的类生成隐式复制构造函数。
B b1 = b;
语句使用隐式生成的复制构造函数。我不明白为什么你期望它会产生错误。然而,引用本身不能被赋值(重新赋值),这就是编译器拒绝为带有引用的类生成隐式复制赋值运算符的原因。编译器通过发出警告来通知您这一点。如果您实际上尝试在程序中对类
B
使用赋值运算符,您最终会遇到错误。在这方面,引用的情况与 const 成员的情况几乎相同:如果某个类具有 const 成员,编译器将毫无问题地生成隐式复制构造函数对于此类,但将拒绝生成隐式赋值。
Constructors in C++ language perform initialization, while assignment operators perform assignment. Initialization and assignment are tow completely different concepts.
References in C++ language can be initialized, which is why the compiler has no problem generating implicit copy constructors for classes with references. The
B b1 = b;
statement uses that implicitly generated copy constructor. I don't see why you expect it to produce an error.However, references themselves cannot be assigned (reassigned), which is why the compiler refuses to generate implicit copy assignment operators for classes with references. The compiler informed you about that by issuing a warning. If you actually try to use the assignment operator for class
B
in your program, you'll end up with an error.In this respect the situation with references in pretty much the same as situation with
const
members: if some class hasconst
members, the compiler will have no problem generating the implicit copy constructor for this class, but will refuse to generate the implicit assignment.引用只能初始化一次,不能更改。构造函数是有效的,因为它初始化了 m_a,但副本会重新分配 m_a,这是禁止的。
References can only be initialized one time, and cannot be changed. The constructor is valid because it initializes m_a, but the copy would reassign the m_a, which is forbidden.
您的示例不需要赋值运算符。 VC++ 只是警告您,如果需要的话,它将无法生成一个。如果您正在编写一个库并且您忘记预料到您的库的用户可能需要复制 B,那么这可能是有用的信息。
如果您不想要警告,您可以抑制它(例如,使用#pragma),或者您可以声明一个私有赋值运算符而不实现它。如果有人尝试调用赋值运算符,它将失败并出现链接时错误。
Your sample doesn't need an assignment operator. VC++ is simply warning you that it wouldn't be able to generate one if it needed to. That can be useful information if you're writing a library and you forgot to anticipate that the user of your library might need to copy B's.
If you don't want the warning, you can suppress it (e.g., with a #pragma), or your can declare a private assignment operator and not implement it. If somebody then tries to invoke the assignment operator, it'll fail with a link-time error.
B b(a);
是一个有效的语句。因为当您将A
类型的对象传递给B
的构造函数时,就会调用B::B(A&)
。顺便说一下,使用 g++ 时,不会生成您提到的警告。 (恕我直言,不应该有任何警告,因为
B b1= b;
调用了默认复制构造函数B::B(const B&)
.)B b(a);
is a valid statement. BecauseB::B(A&)
is invoked when you pass the object of typeA
to the constructor ofB
.By the way with g++ there is no warning generated as you mentioned. (And IMHO, there should not be any warning because
B b1= b;
invoked default copy constructorB::B(const B&)
.)