在为泛型类型的模板 class 重载 operator= 时检查自赋值
Checking for self-assignment when overloading operator= for template class of generic type
通常,重载和赋值运算符时,应该检查自赋值。在一个简单的非模板化 class 中,它需要以下内容:
MyClass& MyClass::operator=(const MyClass& rhs) {
if (this != &rhs) {
// do the assignment
}
return *this;
}
但是,假设 MyClass
是模板化的,并且我们想要泛化 class 可以具有的泛型类型的赋值运算符重载:
template<class T>
class MyClass
{
template<class U>
friend class MyClass;
T value;
template<class U>
MyClass<T>& operator=(const MyClass<U>& rhs)
//... other stuff
}
template<class T>
template<class U>
MyClass<T>& MyClass<T>::operator=(const MyClass<U>& rhs)
{
if (this != &rhs) { //this line gives an error
value = (T)rhs.value;
}
}
在上述情况下,行 if (this != &rhs)
会给出编译器错误。在 MS Visual Studio 2015 中,是错误 C2446:
'==': no conversion from 'const MyClas< T > *' to 'MyClass< U > *const '
因此,在使用可以在右侧获取泛型模板类型 MyClass
实例的赋值运算符时,如何实现自赋值检查?
我建议重载 operator=
。
template<class T>
class MyClass
{
template<class U>
friend class MyClass;
T value;
template<class U>
MyClass& operator=(const MyClass<U>& rhs) { ... }
// Overload for MyClass<T>
MyClass& operator=(const MyClass& rhs) { ... }
};
并仅在第二次重载时检查自赋值。
我想指出,如果 MyClass
的特化不简单,上述逻辑就会中断。例如,如果您使用:
template<> class MyClass<int>:public MyClass<long> { ... };
没有调用检查自分配的代码。参见 http://ideone.com/AqCsa3。
我肯定很想看到
Came to MyClass<T>::operator=(const MyClass& rhs)
作为该程序的输出。
same_object
是一个接受两个引用的函数,如果它们都引用同一个对象,则 return 为真; 地址不相同,但对象相同。
template<class T, class U, class=void>
struct same_object_t {
constexpr bool operator()(T const volatile&, U const volatile&)const{return false;}
};
template<class T>
struct same_object_t<T,T,void> {
bool operator()(T const volatile& lhs, T const volatile& rhs)const{
return std::addressof(lhs) == std::addressof(rhs);
}
};
template<class T, class U>
struct same_object_t<T,U,
typename std::enable_if<
std::is_base_of<T, U>::value && !std::is_same<T,U>::value
>::type
>:
same_object_t<T,T>
{};
template<class T, class U>
struct same_object_t<T,U,
typename std::enable_if<
std::is_base_of<U, T>::value && !std::is_same<T,U>::value
>::type
>:
same_object_t<U,U>
{};
template<class T, class U>
constexpr bool same_object(T const volatile& t, U const volatile& u) {
return same_object_t<T,U>{}(t, u);
}
template<class T>
template<class U>
MyClass<T>& MyClass<T>::operator=(const MyClass<U>& rhs)
{
if (!same_object(*this, rhs)) {
value = static_cast<T>(rhs.value);
}
return *this;
}
由于联合和标准布局,两个不同的对象可以共享一个地址 "first member" 地址共享,以及一个数组和数组的第一个元素。来自 same_object
.
的案例 return false
private
/protected
继承可以打破这一点,通过多个路径从类型 T 继承的类型 U 也可以。
通常,重载和赋值运算符时,应该检查自赋值。在一个简单的非模板化 class 中,它需要以下内容:
MyClass& MyClass::operator=(const MyClass& rhs) {
if (this != &rhs) {
// do the assignment
}
return *this;
}
但是,假设 MyClass
是模板化的,并且我们想要泛化 class 可以具有的泛型类型的赋值运算符重载:
template<class T>
class MyClass
{
template<class U>
friend class MyClass;
T value;
template<class U>
MyClass<T>& operator=(const MyClass<U>& rhs)
//... other stuff
}
template<class T>
template<class U>
MyClass<T>& MyClass<T>::operator=(const MyClass<U>& rhs)
{
if (this != &rhs) { //this line gives an error
value = (T)rhs.value;
}
}
在上述情况下,行 if (this != &rhs)
会给出编译器错误。在 MS Visual Studio 2015 中,是错误 C2446:
'==': no conversion from 'const MyClas< T > *' to 'MyClass< U > *const '
因此,在使用可以在右侧获取泛型模板类型 MyClass
实例的赋值运算符时,如何实现自赋值检查?
我建议重载 operator=
。
template<class T>
class MyClass
{
template<class U>
friend class MyClass;
T value;
template<class U>
MyClass& operator=(const MyClass<U>& rhs) { ... }
// Overload for MyClass<T>
MyClass& operator=(const MyClass& rhs) { ... }
};
并仅在第二次重载时检查自赋值。
我想指出,如果 MyClass
的特化不简单,上述逻辑就会中断。例如,如果您使用:
template<> class MyClass<int>:public MyClass<long> { ... };
没有调用检查自分配的代码。参见 http://ideone.com/AqCsa3。
我肯定很想看到
Came to MyClass<T>::operator=(const MyClass& rhs)
作为该程序的输出。
same_object
是一个接受两个引用的函数,如果它们都引用同一个对象,则 return 为真; 地址不相同,但对象相同。
template<class T, class U, class=void>
struct same_object_t {
constexpr bool operator()(T const volatile&, U const volatile&)const{return false;}
};
template<class T>
struct same_object_t<T,T,void> {
bool operator()(T const volatile& lhs, T const volatile& rhs)const{
return std::addressof(lhs) == std::addressof(rhs);
}
};
template<class T, class U>
struct same_object_t<T,U,
typename std::enable_if<
std::is_base_of<T, U>::value && !std::is_same<T,U>::value
>::type
>:
same_object_t<T,T>
{};
template<class T, class U>
struct same_object_t<T,U,
typename std::enable_if<
std::is_base_of<U, T>::value && !std::is_same<T,U>::value
>::type
>:
same_object_t<U,U>
{};
template<class T, class U>
constexpr bool same_object(T const volatile& t, U const volatile& u) {
return same_object_t<T,U>{}(t, u);
}
template<class T>
template<class U>
MyClass<T>& MyClass<T>::operator=(const MyClass<U>& rhs)
{
if (!same_object(*this, rhs)) {
value = static_cast<T>(rhs.value);
}
return *this;
}
由于联合和标准布局,两个不同的对象可以共享一个地址 "first member" 地址共享,以及一个数组和数组的第一个元素。来自 same_object
.
false
private
/protected
继承可以打破这一点,通过多个路径从类型 T 继承的类型 U 也可以。