想象一下以下示例:
#include <iostream>
#include <vector>
struct Base
{
virtual void foo() = 0;
};
struct A : public Base
{
void foo() override {};
};
struct B : public Base
{
void foo() override {};
};
template <typename T>
struct C
{
struct Element
{
int x, y, z;
bool operator==(const Element& e)
{
if (x != e.x) return false;
if (y != e.y) return false;
if (z != e.z) return false;
return true;
}
};
Base* p;
std::vector<Element> v;
C()
{
p = new T();
}
void add(int x, int y, int z)
{
Element e;
e.x = x;
e.y = y;
e.z = z;
v.push_back(e);
}
void remove(int x, int y, int z)
{
Element e;
e.x = x;
e.y = y;
e.z = z;
std::vector<Element>::iterator it = std::find(v.begin(), v.end(), e);
if (p != v.end()) v.erase(p);
}
void print()
{
for (Element e : v) std::cout << e.x << " " << e.y << " " << e.z << std::endl;
}
};
int main()
{
C<A> a;
a.add(1, 2, 3);
a.add(4, 5, 6);
a.add(7, 8, 9);
a.remove(4, 5, 6);
a.print();
return 0;
}
现在让我们在C中添加一个拷贝构造函数,这样我们就可以用另一个保存另一个数据类型的C来初始化C(只要数据类型派生自Base)。目标是使这成为可能:
int main()
{
C<A> a;
a.add(1, 2, 3);
a.add(4, 5, 6);
a.add(7, 8, 9);
a.remove(4, 5, 6);
a.print();
C<B> b(a); // <----- This should be possible.
return 0;
}
我试图像这样解决它:
template <typename U>
C(const C<U>& c)
{
p = new U(*c.p);
v = c.v;
}
但是我从Visual Studio中得到了这两个错误:
错误C2679二进制'=':找不到运算符,它接受类型为'const std :: vector :: Element,std :: allocator&lt; _Ty&gt;&gt;'的右侧操作数(或者没有可接受的转换)
错误C2664'A :: A(A&amp;&amp;)':无法将参数1从'Base'转换为'const A&amp;'
根据我的理解,std :: vector已经实现了一个赋值操作符,它应该在操作符的右侧创建一个向量的深层副本。
那么我做错了什么,我需要做些什么才能让它发挥作用?
答案 0 :(得分:2)
v = c.v;
这是两种完全不同的类型。
v
是std::vector<C<T>::Element>
。
c.v
是std::vector<C<U>::Element>
。
鉴于不同的T
和U
类型,C<T>
和C<U>
是完全不同的类型,彼此无关。
C<T>::Element
和C<U>::Element
也是完全不同的类型。每个std::vector
也是如此。
C<T>::Element
和C<U>::Element
可能具有相同的类名。他们可能拥有完全相同的班级成员。但它们是两种独立的类型,因为class A
与class B
不同,彼此之间也是不同的。
问题不在于模板拷贝构造函数本身。问题是拷贝构造函数试图为彼此分配一种类型。
您需要做的是删除内部类Element
。它不依赖于模板参数。使其成为一个独立的顶级课程。然后,C<T>
和C<U>
都会有一个类成员v
,它是相同类型的向量,并且可以相互分配。