如果 class 只有一个(模板化的)移动赋值运算符,为什么可以进行复制赋值?
Why is copy assigment possible, if a class has only a (templated) move assignment operator?
我今天被代码绊倒了,我不明白。请考虑以下示例:
#include <iostream>
#include <string>
class A
{
public:
template <class Type>
Type& operator=(Type&& theOther)
{
text = std::forward<Type>(theOther).text;
return *this;
}
private:
std::string text;
};
class B
{
public:
B& operator=(B&& theOther)
{
text = std::forward<B>(theOther).text;
return *this;
}
private:
std::string text;
};
int main()
{
A a1;
A a2;
a2 = a1;
B b1;
B b2;
b2 = b1;
return 0;
}
编译时,MinGW-w64/g++ 10.2 状态:
..\src\Main.cpp: In function 'int main()':
..\src\Main.cpp:41:7: error: use of deleted function 'B& B::operator=(const B&)'
41 | b2 = b1;
| ^~
..\src\Main.cpp:19:7: note: 'B& B::operator=(const B&)' is implicitly declared as deleted because 'B' declares a move constructor or move assignment operator
19 | class B
| ^
mingw32-make: *** [Makefile:419: Main.o] Error 1
我完全理解错误信息。但我不明白为什么我没有收到与 class A
相同的消息。模板化移动赋值运算符不也是移动赋值运算符吗?那么为什么复制赋值运算符没有被删除呢?这段代码写得好吗?
Isn't the templated move assignment operator also a move assignment operator?
不,它不被视为 move assignment operator。
(强调我的)
A move assignment operator of class T is a non-template non-static member function with the name operator=
that takes exactly one parameter of type T&&
, const T&&
, volatile T&&
, or const volatile T&&
.
作为效果,A
仍然具有隐式声明的 copy/move 赋值运算符。
顺便说一句:您的模板赋值运算符采用 forwarding reference,它可以接受左值和右值。在 a2 = a1;
中,它在重载决策中胜过生成的复制赋值运算符并被调用。
作为对@songyuanyao 基于标准的回答的补充:这类语言规则是 MISRA/AUTOSAR(安全关键 C++ 开发的语言指南)等指南具有“避免开发人员混淆”规则的常见原因如:
(from AUTOSAR C++14 Guidelines)
Rule A14-5-1 (required, implementation, automated)
A template constructor shall not participate in overload resolution
for a single argument of the enclosing class type.
Rationale
A template constructor is never a copy or move constructor and
therefore doesn’t prevent the implicit definition of a copy or move
constructor even if the template constructor looks similar and might
easily be confused. At the same time, copy or move operations do not
necessarily only use a copy or move constructor, but go through the
normal overload resolution process to find the best matching function
to use. This can cause confusion in the following cases:
- a template constructor that looks like a copy/move constructor is not selected
- for a copy/move operation because the compiler has generated an implicit copy/move constructor as well a template constructor is
selected in preference over a copy/move constructor because the
template constructor is a better match
To avoid these confusing situations, template constructors shall not
participate in overload resolution for a single argument of the
enclosing class type to avoid a template constructor being selected
for a copy/move operation. It also makes it clear that the constructor
is not a copy/move constructor and that it does not prevent the
implicit generation of copy/move constructors.
Rule M14-5-3 (required, implementation, automated)
A copy assignment operator shall be declared when there is a template
assignment operator with a parameter that is a generic parameter.
也就是说,对于开发人员来说,模板 copy/move ctor/assignment 运算符不会抑制(/不会“激活”规则 5)隐式生成的运算符可能会令人惊讶。您通常需要使用 SFINAE 来确保模板化的 ctor/assignment op 不会表现得 就好像 它是 copy/move ctor/assignment 通过允许重载对封闭 class.
的单个参数有效
我今天被代码绊倒了,我不明白。请考虑以下示例:
#include <iostream>
#include <string>
class A
{
public:
template <class Type>
Type& operator=(Type&& theOther)
{
text = std::forward<Type>(theOther).text;
return *this;
}
private:
std::string text;
};
class B
{
public:
B& operator=(B&& theOther)
{
text = std::forward<B>(theOther).text;
return *this;
}
private:
std::string text;
};
int main()
{
A a1;
A a2;
a2 = a1;
B b1;
B b2;
b2 = b1;
return 0;
}
编译时,MinGW-w64/g++ 10.2 状态:
..\src\Main.cpp: In function 'int main()':
..\src\Main.cpp:41:7: error: use of deleted function 'B& B::operator=(const B&)'
41 | b2 = b1;
| ^~
..\src\Main.cpp:19:7: note: 'B& B::operator=(const B&)' is implicitly declared as deleted because 'B' declares a move constructor or move assignment operator
19 | class B
| ^
mingw32-make: *** [Makefile:419: Main.o] Error 1
我完全理解错误信息。但我不明白为什么我没有收到与 class A
相同的消息。模板化移动赋值运算符不也是移动赋值运算符吗?那么为什么复制赋值运算符没有被删除呢?这段代码写得好吗?
Isn't the templated move assignment operator also a move assignment operator?
不,它不被视为 move assignment operator。
(强调我的)
A move assignment operator of class T is a non-template non-static member function with the name
operator=
that takes exactly one parameter of typeT&&
,const T&&
,volatile T&&
, orconst volatile T&&
.
作为效果,A
仍然具有隐式声明的 copy/move 赋值运算符。
顺便说一句:您的模板赋值运算符采用 forwarding reference,它可以接受左值和右值。在 a2 = a1;
中,它在重载决策中胜过生成的复制赋值运算符并被调用。
作为对@songyuanyao 基于标准的回答的补充:这类语言规则是 MISRA/AUTOSAR(安全关键 C++ 开发的语言指南)等指南具有“避免开发人员混淆”规则的常见原因如:
(from AUTOSAR C++14 Guidelines)
Rule A14-5-1 (required, implementation, automated)
A template constructor shall not participate in overload resolution for a single argument of the enclosing class type.
Rationale
A template constructor is never a copy or move constructor and therefore doesn’t prevent the implicit definition of a copy or move constructor even if the template constructor looks similar and might easily be confused. At the same time, copy or move operations do not necessarily only use a copy or move constructor, but go through the normal overload resolution process to find the best matching function to use. This can cause confusion in the following cases:
- a template constructor that looks like a copy/move constructor is not selected
- for a copy/move operation because the compiler has generated an implicit copy/move constructor as well a template constructor is selected in preference over a copy/move constructor because the template constructor is a better match
To avoid these confusing situations, template constructors shall not participate in overload resolution for a single argument of the enclosing class type to avoid a template constructor being selected for a copy/move operation. It also makes it clear that the constructor is not a copy/move constructor and that it does not prevent the implicit generation of copy/move constructors.
Rule M14-5-3 (required, implementation, automated)
A copy assignment operator shall be declared when there is a template assignment operator with a parameter that is a generic parameter.
也就是说,对于开发人员来说,模板 copy/move ctor/assignment 运算符不会抑制(/不会“激活”规则 5)隐式生成的运算符可能会令人惊讶。您通常需要使用 SFINAE 来确保模板化的 ctor/assignment op 不会表现得 就好像 它是 copy/move ctor/assignment 通过允许重载对封闭 class.
的单个参数有效