我有一个模板Base
类,我想继承它。 Base
类有一个公共方法,它假设模板类型。代码的简化版本如下:
#include <iostream>
using namespace std;
template <typename V>
class Base {
public:
virtual void print() {
a = "a";
cout << "Base class" ;
}
void callIt() {
print();
}
V a;
};
class Derived : public Base<int> {
public:
void print() override {
cout << "Derived class\n";
}
};
int main() {
Derived d;
d.callIt();
return 0;
}
使用GCC 6.2.0编译此代码会出现此错误:
test.cpp: In instantiation of 'void Base<V>::print() [with V = int]':
test.cpp:13:10: required from 'void Base<V>::callIt() [with V = int]'
test.cpp:28:12: required from here
test.cpp:9:7: error: invalid conversion from 'const char*' to 'int' [-fpermissive]
a = "a";
~~^~~~~
我想从编译器中完全隐藏Base
方法的print()
类实现。我怎么能这样做?
编辑:具有讽刺意味的是,注释掉行a = "a";
可以让代码成功编译并打印出Derived class
。
答案 0 :(得分:4)
如果在声明Base<int>::print()
之前为Derived
添加专门化,则可以使代码正常工作:
#include <iostream>
using namespace std;
template <typename V>
class Base {
public:
virtual void print() {
a = "a";
cout << "Base class" ;
}
void callIt() {
print();
}
V a;
};
template <>
void Base<int>::print() {
cout << "Specialization\n";
}
class Derived : public Base<int> {
public:
void print() override {
cout << "Derived class\n";
}
};
int main() {
Derived d;
d.callIt();
return 0;
}
小心这样做:它打破了Base
的作者关于哪些类型与模板一起工作的假设,并且很容易导致库代码中难以追踪的错误。
答案 1 :(得分:2)
由于print
是虚拟的,因此只要从Base
继承,就必须实例化它(因为编译器需要生成适当的vtable) - 即使函数尚未被调用。如果print
是非虚拟成员,则情况并非如此 - 在这种情况下,只有在被调用时才会实例化。
我能想到的唯一解决方案是看看你是否可以在这里摆脱虚拟功能 - 如果你不能,抱歉,你运气不好。
答案 2 :(得分:0)
简化以解决问题:
template <typename V>
class Base {
public:
virtual void print() {
a = "a";
}
V a;
};
int main()
{
Base<int> b;
}
结果:
<source>:5:7: error: invalid conversion from 'const char*' to 'int' [-fpermissive]
a = "a";
这是因为我们实际上在说:
int a = "a";
当然这是一个错误,因为您无法将const char*
分配给int
。
因此,在此处得出的唯一合理结论是,Base<T>
的设计遵循约束条件,即必须存在从const char*
到T
的可访问转化。
如果你想让T
成为其他东西,那么这个基类是不合适的,你需要找到另一种方法来实现你想要的东西(封装?)。