我想确保赋值运算符中的* this!=& rhs。但它不会编译。有什么建议吗?
template <typename T>
class A {
public:
A() {
std::cout << "Default Constructor" << std::endl;
}
A(const T& t) : m_t(t) {
std::cout << "Templated Constructor" << std::endl;
}
template <typename X>
A( const A<X>& rhs ) : m_t( (static_cast< A<T> >(rhs)).m_t ) {
std::cout << "Copy Constructor" << std::endl;
}
template <typename X>
const A& operator=( A<X>& rhs) {
std::cout << "Assignment Operator" << std::endl;
if (this != static_cast< A<T>* > (&rhs) )
m_t = rhs.get();
return *this;
}
T get() { return m_t; }
private:
T m_t;
};
class base {};
class derived : public base {};
int main()
{
A<base*> test1;
A<derived*> test2;
test1 = test2;
}
答案 0 :(得分:2)
你在这里想做什么
if (this != static_cast< A<T>* > (&rhs) )
从static_cast
到A<derived*>
执行A<base*>
。
以下是static_cast
的作用:
您可以显式转换指针 类型A的指针B类型的指针 如果A是B的基类,如果A不是 B的基类,编译错误 会结果。
A<base*>
不是A<derived*>
的基类,因此错误。
一般来说,即使A<T>
可转换为A<X>
,X
也永远不会是T
的基类。因此,演员阵容不合时宜。
解决方案是使用reinterpret_cast<void*>(&rhs)
代替。
我更多地研究了这个;这是结果。我先给出代码,然后发表评论。
template <typename T>
class Aggregator {
public:
Aggregator() {
std::cout << "Default Constructor" << std::endl;
}
Aggregator(const T& t) : m_t(t) {
std::cout << "Constructor With Argument" << std::endl;
}
Aggregator& operator= (const Aggregator& rhs)
{
std::cout << "Assignment Operator (same type)";
if (this->get() == rhs.get()) {
std::cout << " -- SKIPPED assignment";
}
else {
T justForTestingCompilation = rhs.get();
}
std::cout << std::endl;
return *this;
}
template <class U>
Aggregator& operator=(const Aggregator<U>& rhs)
{
std::cout << "Assignment Operator (template)";
if (this->get() == rhs.get()) {
std::cout << " -- SKIPPED assignment";
}
else {
T justForTestingCompilation = rhs.get();
}
std::cout << std::endl;
return *this;
}
T get() const { return m_t; }
private:
T m_t;
};
class base {};
class derived : public base {};
class unrelated {};
// This is just for the code to compile; in practice will always return false
bool operator==(const base& lhs, const base& rhs) { return &lhs == &rhs; }
到目前为止发生的重要事项:
iff
它们的“正常”,你应该做什么样的版本。Aggregate<T>
分配给Aggregate<T>
,第二个用于将Aggregate<T1>
分配给Aggregate<T2>
。这直接来自Tony的回答,这是“正确的方式”。operator==
可以伪造一个比较运算符,用于没有隐式定义的类型。具体来说,当Aggregate<U>
不是基本类型时,我们需要对包含U
的代码进行编译。以下是所有乐趣的地方:
int main(int argc, char* argv[])
{
base b;
derived d;
unrelated u;
Aggregator<base*> aggPB(&b);
Aggregator<base*> aggPBDerivedInstance(&d);
Aggregator<derived*> aggPD(&d);
Aggregator<unrelated*> aggPU(&u);
Aggregator<base> aggB(b);
Aggregator<base> aggBDerivedInstance(d); // slicing occurs here
Aggregator<derived> aggD(d);
Aggregator<unrelated> aggU(u);
std::cout << "1:" << std::endl;
// base* = base*; should compile, but SKIP assignment
// Reason: aggregate values are the same pointer
aggPB = aggPB;
// base = base; should compile, perform assignment
// Reason: aggregate values are different copies of same object
aggB = aggB;
std::cout << "2:" << std::endl;
// base* = base*; should compile, perform assignment
// Reason: aggregate values are pointers to different objects
aggPB = aggPBDerivedInstance;
// base = base; should compile, perform assignment
// Reason: aggregate values are (copies of) different objects
aggB = aggBDerivedInstance;
std::cout << "3:" << std::endl;
// base* = derived*; should compile, perform assignment
// Reason: aggregate values are pointers to different objects
aggPB = aggPD;
// base = derived; should compile, perform assignment (SLICING!)
// Reason: derived is implicitly convertible to base, aggregates are (copies of) different objects
aggB = aggD;
std::cout << "4:" << std::endl;
// base* = derived*; should compile, but SKIP assignment
// Reason: aggregate values are (differently typed) pointers to same object
aggPBDerivedInstance = aggPD;
// base = derived; should compile, perform assignment (SLICING!)
// Reason: derived is implicitly convertible to base, aggregates are (copies of) different objects
aggBDerivedInstance = aggD;
std::cout << "5:" << std::endl;
// derived* = base*; should NOT compile
// Reason: base* not implicitly convertible to derived*
// aggPD = aggPB;
// derived = base; should NOT compile
// Reason: base not implicitly convertible to derived
// aggD = aggB;
return 0;
}
这将输出:
Constructor With Argument
Constructor With Argument
Constructor With Argument
Constructor With Argument
Constructor With Argument
Constructor With Argument
Constructor With Argument
Constructor With Argument
1:
Assignment Operator (same type) -- SKIPPED assignment
Assignment Operator (same type)
2:
Assignment Operator (same type)
Assignment Operator (same type)
3:
Assignment Operator (template)
Assignment Operator (template)
4:
Assignment Operator (template) -- SKIPPED assignment
Assignment Operator (template)
5:
那么......我们从中学到了什么?
我要说这意味着相等检查是多余的,应该删除。
但是,如果:
T1
和T2
属于同一类型或存在隐式转化,和 T1
的赋值运算符或T2
的转换运算符很昂贵(这会立即将原语从图片中删除),和 bool operator== (const T1& lhs, const T2& rhs)
然后检查相等可能有意义(取决于您期望operator==
返回true
的频率。)
如果你打算只使用Aggregator<T>
指针类型(或任何其他原语),那么相等测试是多余的。
如果您打算使用昂贵的构造类类型,那么您将需要一个有意义的相等运算符来使用它们。如果您也将它用于不同类型,则还需要转换运算符。
答案 1 :(得分:2)
如果真的困扰你,你总是可以拥有第二个不需要演员的非模板operator=
。为了避免重复,如果这个!=&amp; rhs它可以显式调用模板版本。说明如何调用右操作符的示例:
#include <iostream>
template <class T>
struct X
{
X& operator=(X& rhs)
{
std::cout << "non-template " << (this == &rhs ? "self\n" : "other\n");
}
template <class U>
X& operator=(X<U>& rhs)
{
std::cout << "template\n";
}
};
int main()
{
X<int> x;
x = x;
X<int> y;
x = y;
X<double> z;
x = z;
}
答案 2 :(得分:1)
在您的情况下,无需测试自我分配。与一些教程可能提出的建议相反,自我指派测试对于重载赋值运算符一般不是必不可少的。
只有当(实现不当)赋值运算符首先释放资源,然后创建新资源作为右侧操作数的资源副本时,才需要这样做。只有这样,自我分配才会变成灾难性的,因为左手对象会同时释放右手操作数(本身)的资源。
poor_assignment& operator=(const poor_assignment& rhv)
{
this->Release(); // == rhv.Release() in case of self-assignment
this->Create(rhv.GetResources());
}
答案 3 :(得分:0)
好的版本:实现与类本身具有完全相同类型的复制赋值运算符:
const A& operator=( A<T>& rhs) {
std::cout << "copy assignment operator" << std::endl;
if(this != &rhs)
m_t = rhs.m_t;
return *this;
}
'dirty'版本:将每个对象的地址转换为intptr_t
并比较普通值:
template<class X>
const A& operator=( A<X>& rhs) {
std::cout << "Assignment Operator" << std::endl;
if((intptr_t)(this) != (intptr_t)(&rhs))
m_t = rhs.get();
return *this;
}
编辑:实际上,第二个版本不起作用,因为编译器生成的复制赋值运算符代替使用。所以你自己实施一个。 - 结束编辑
此外,您不需要使用rhs.get()
,只需使用rhs.m_t
,因为您可以从类本身访问私有成员。
答案 4 :(得分:0)
我个人认为以下是最优雅的解决方案。我不知道为什么我没有得到它 - 我最初使用流血的c ++编译器,它似乎失败了 - 但g ++这是最干净的?
如果有人不同意我,我会删除我的答案并将其交给其他人。请注意,A *实际上是指A *,但需要注意
template <typename X>
const A& operator=( A<X>& rhs) {
std::cout << "Assignment Operator" << std::endl;
if (this != reinterpret_cast< A* >(&rhs))
m_t = rhs.get();
return *this;
}