我听说c++的类成员函数模板不能是虚的。这是真的吗?
如果它们可以是虚拟的,那么有什么场景可以使用这样的函数呢?
我听说c++的类成员函数模板不能是虚的。这是真的吗?
如果它们可以是虚拟的,那么有什么场景可以使用这样的函数呢?
当前回答
在其他答案中,建议的模板函数是一个门面,并不能提供任何实际的好处。
模板函数对于只编写一次代码很有用 不同的类型。 虚函数对于为不同的类提供公共接口非常有用。
该语言不允许虚拟模板函数,但通过一个变通方法,可以同时拥有两者,例如,每个类都有一个模板实现和一个虚拟公共接口。
但是,有必要为每个模板类型组合定义一个虚拟包装器函数:
#include <memory>
#include <iostream>
#include <iomanip>
//---------------------------------------------
// Abstract class with virtual functions
class Geometry {
public:
virtual void getArea(float &area) = 0;
virtual void getArea(long double &area) = 0;
};
//---------------------------------------------
// Square
class Square : public Geometry {
public:
float size {1};
// virtual wrapper functions call template function for square
virtual void getArea(float &area) { getAreaT(area); }
virtual void getArea(long double &area) { getAreaT(area); }
private:
// Template function for squares
template <typename T>
void getAreaT(T &area) {
area = static_cast<T>(size * size);
}
};
//---------------------------------------------
// Circle
class Circle : public Geometry {
public:
float radius {1};
// virtual wrapper functions call template function for circle
virtual void getArea(float &area) { getAreaT(area); }
virtual void getArea(long double &area) { getAreaT(area); }
private:
// Template function for Circles
template <typename T>
void getAreaT(T &area) {
area = static_cast<T>(radius * radius * 3.1415926535897932385L);
}
};
//---------------------------------------------
// Main
int main()
{
// get area of square using template based function T=float
std::unique_ptr<Geometry> geometry = std::make_unique<Square>();
float areaSquare;
geometry->getArea(areaSquare);
// get area of circle using template based function T=long double
geometry = std::make_unique<Circle>();
long double areaCircle;
geometry->getArea(areaCircle);
std::cout << std::setprecision(20) << "Square area is " << areaSquare << ", Circle area is " << areaCircle << std::endl;
return 0;
}
输出:
方形面积为1,圆形面积为3.1415926535897932385
在这里试试
其他回答
不,他们不能。但是:
template<typename T>
class Foo {
public:
template<typename P>
void f(const P& p) {
((T*)this)->f<P>(p);
}
};
class Bar : public Foo<Bar> {
public:
template<typename P>
void f(const P& p) {
std::cout << p << std::endl;
}
};
int main() {
Bar bar;
Bar *pbar = &bar;
pbar -> f(1);
Foo<Bar> *pfoo = &bar;
pfoo -> f(1);
};
如果您想要做的只是拥有一个公共接口并将实现推迟到子类,则效果大致相同。
不可以,模板成员函数不能为虚函数。
如果预先知道模板方法的类型集,则'虚拟模板方法'有一个变通方法。
为了说明这个想法,在下面的例子中只使用了两种类型(int和double)。
在那里,一个“虚拟”模板方法(Base:: method)调用相应的虚拟方法(Base:: VMethod之一),后者反过来调用模板方法实现(Impl::TMethod)。
只需要在派生实现(AImpl, BImpl)中实现模板方法TMethod,并使用derived <*Impl>。
class Base
{
public:
virtual ~Base()
{
}
template <typename T>
T Method(T t)
{
return VMethod(t);
}
private:
virtual int VMethod(int t) = 0;
virtual double VMethod(double t) = 0;
};
template <class Impl>
class Derived : public Impl
{
public:
template <class... TArgs>
Derived(TArgs&&... args)
: Impl(std::forward<TArgs>(args)...)
{
}
private:
int VMethod(int t) final
{
return Impl::TMethod(t);
}
double VMethod(double t) final
{
return Impl::TMethod(t);
}
};
class AImpl : public Base
{
protected:
AImpl(int p)
: i(p)
{
}
template <typename T>
T TMethod(T t)
{
return t - i;
}
private:
int i;
};
using A = Derived<AImpl>;
class BImpl : public Base
{
protected:
BImpl(int p)
: i(p)
{
}
template <typename T>
T TMethod(T t)
{
return t + i;
}
private:
int i;
};
using B = Derived<BImpl>;
int main(int argc, const char* argv[])
{
A a(1);
B b(1);
Base* base = nullptr;
base = &a;
std::cout << base->Method(1) << std::endl;
std::cout << base->Method(2.0) << std::endl;
base = &b;
std::cout << base->Method(1) << std::endl;
std::cout << base->Method(2.0) << std::endl;
}
输出:
0
1
2
3
注: Base::Method对于实际代码来说实际上是多余的(VMethod可以被设为public并直接使用)。 我添加它,使它看起来像一个实际的“虚拟”模板方法。
至少在gcc 5.4中,虚函数可以是模板成员,但必须是模板本身。
#include <iostream>
#include <string>
class first {
protected:
virtual std::string a1() { return "a1"; }
virtual std::string mixt() { return a1(); }
};
class last {
protected:
virtual std::string a2() { return "a2"; }
};
template<class T> class mix: first , T {
public:
virtual std::string mixt() override;
};
template<class T> std::string mix<T>::mixt() {
return a1()+" before "+T::a2();
}
class mix2: public mix<last> {
virtual std::string a1() override { return "mix"; }
};
int main() {
std::cout << mix2().mixt();
return 0;
}
输出
mix before a2
Process finished with exit code 0
下面的代码可以在windows 7上使用mingwg++ 3.4.5编译并正常运行:
#include <iostream>
#include <string>
using namespace std;
template <typename T>
class A{
public:
virtual void func1(const T& p)
{
cout<<"A:"<<p<<endl;
}
};
template <typename T>
class B
: public A<T>
{
public:
virtual void func1(const T& p)
{
cout<<"A<--B:"<<p<<endl;
}
};
int main(int argc, char** argv)
{
A<string> a;
B<int> b;
B<string> c;
A<string>* p = &a;
p->func1("A<string> a");
p = dynamic_cast<A<string>*>(&c);
p->func1("B<string> c");
B<int>* q = &b;
q->func1(3);
}
输出为:
A:A<string> a
A<--B:B<string> c
A<--B:3
后来我又添加了一个新类X:
class X
{
public:
template <typename T>
virtual void func2(const T& p)
{
cout<<"C:"<<p<<endl;
}
};
当我试图在main()中像这样使用类X时:
X x;
x.func2<string>("X x");
g++报告以下错误:
vtempl.cpp:34: error: invalid use of `virtual' in template declaration of `virtu
al void X::func2(const T&)'
所以很明显:
虚成员函数可以在类模板中使用。编译器可以很容易地构造虚表 将类模板成员函数定义为虚函数是不可能的,如你所见,很难确定函数签名和分配虚表项。