假设我有以下内容:
#include <iostream>
#include <string>
template<class T>
class base
{
public:
void print()
{
T t = get();
std::cout << t << std::endl;
}
virtual T get() const
{
// assumes T can be constructed from, say, -1
T t = -1.0;
return t;
}
};
class derived : public base<std::string>
{
public:
virtual std::string get() const
{
// this is a silly example, but one can
// imagine that what we return here could
// depend on data members of derived
return "this is a string";
}
};
int main()
{
derived d;
d.print();
return 0;
}
在我看来,d.print()
应该调用derived::get()
,因为get()
是虚拟的。但是,我收到编译错误,说我无法将string
初始化为-1.0
,这意味着当我调用{{1}时编译器正试图调用base::get()
}。发生了什么事?
答案 0 :(得分:6)
但是,我收到编译错误,说我无法将字符串初始化为-1.0,这意味着当我调用d.print()时,编译器正在尝试调用base :: get()。 / p>
不,编译器错误意味着编译器正在尝试实例化必须所做的base<std::string>::get()
,因为derived
使用base<std::string>
作为基类。仅仅因为你没有调用函数并不意味着你不能。您仍然可以直接致电base<std::string>::get()
。
您实例化base<std::string>
并将其用作基类。由于base<std::string>::get()
是一个虚函数,因此您将base<std::string>
用作基类这一事实被视为“使用”。由于它正在使用中,因此必须进行实例化。因此编译器必须并将尝试编译该函数。
由于std::string
不能从float隐式构造,因此编译器会因失败的模板替换而出错。
答案 1 :(得分:4)
当你隐式实例化一个类模板时,只会那些使用的成员函数被实例化,正如你已经知道的那样,否则你就不会问这个。问题是标准中 use 的定义可能不完全符合您的预期,特别是,任何虚拟函数使用如果它不是纯
§3.2p2[...]虚拟成员函数如果不是纯粹的,则会被使用。[...]
这意味着即使您的代码没有显式调用它,也会使用base::get
,因此编译器将隐式实例化它并触发您看到的编译器错误。
答案 2 :(得分:1)
T t = -1.0;
当然,如果T
为std::string
,则无法编译。它与get
virtual
无关,而且在运行时将调用哪个函数。运行时在代码编译成机器代码后出现,而您的代码甚至无法编译。
你为什么不这样做:
T t = T();
同样需要T
才能拥有默认构造函数。
答案 3 :(得分:1)
问题出在这个方法中(其中T = std :: string):
virtual T get() const
{
// assumes T can be constructed from, say, -1
T t = -1.0;
return t;
}
编译器是对的。您不能使用double值初始化std :: string。