你想在这里做什么
if (this != static_cast< A<T>* > (&rhs) )
是执行static_cast
从 aA<derived*>
到 a 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; }
到目前为止发生的事情的要点:
- 我没有任何复制构造函数。在现实生活中,我们会将赋值逻辑移动到复制构造函数中,并使用copy 和 swap实现赋值运算符。暂时保持代码简短(er)。
- 赋值运算符实际上并没有做任何事情,但他们会编译
iff
他们的“正常”,做你应该做的版本。
- 有两个赋值运算符;第一个分配
Aggregate<T>
给Aggregate<T>
,第二个分配Aggregate<T1>
给Aggregate<T2>
。这直接来自托尼的回答,而且是“正确的方式”。
- 免费
operator==
的可以为未隐式定义的类型伪造比较运算符。具体来说,我们需要那些包含Aggregate<U>
编译 whenU
不是原始类型的代码。
练习代码
这就是所有乐趣所在:
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)
与上述赋值/转换运算符相比,运行时成本要小得多的A
然后检查是否相等可能有意义(取决于您希望operator==
返回的频率true
)。
结论
如果您打算Aggregator<T>
只使用指针类型(或任何其他原语),那么相等测试是多余的。
如果您打算使用它来构造类类型,那么您将需要一个有意义的相等运算符来配合它们。如果您也将它与不同的类型一起使用,则还需要转换运算符。