我听说c++的类成员函数模板不能是虚的。这是真的吗?

如果它们可以是虚拟的,那么有什么场景可以使用这样的函数呢?


当前回答

我看了所有的14个答案,有些有原因,为什么虚拟模板的功能不能工作,其他人显示了一个工作周围。一个答案甚至表明虚类可以有虚函数。这不足为奇。

我的回答将给出一个直接的理由,为什么标准不允许虚模板函数。因为很多人都在抱怨。首先,我不敢相信有人说虚函数可以在编译时推导出来。这是我听过的最蠢的话。

不管怎样。我确定标准规定指向对象的this指针是其成员函数的第一个参数。

struct MyClass
{
 void myFunction();
}

// translate to
void myFunction(MyClass*);

既然我们都清楚了。然后,我们需要知道模板的转换规则。模板化的参数在它可以隐式转换的内容上受到极大的限制。我不记得所有的内容,但是你可以查看c++ Primer以获得完整的参考。例如,T*可转换为const T*。数组可以转换为指针。但是,派生类不能作为模板形参转换为基类。

struct A {};
struct B : A {};

template<class T>
void myFunction(T&);

template<>
void myFunction<A>(A&) {}

int main()
{
 A a;
 B b;

 myFunction(a); //compiles perfectly
 myFunction((A&)b); // compiles nicely
 myFunction(b); //compiler error, use of undefined template function
}

我希望你们能明白我的意思。你不能使用虚拟模板函数,因为就编译器而言,它们是两个完全不同的函数;作为隐式参数,此参数具有不同的类型。

虚拟模板不能工作的另一个原因同样有效。因为虚表是快速实现虚函数的最佳方式。

其他回答

不可以,模板成员函数不能为虚函数。

在虚函数的情况下如何调用正确的函数?

虚表将包含类的每个虚函数的条目,在运行时,它将选择特定函数的地址,并调用各自的函数。

如何正确的函数必须被调用在虚拟情况下连同函数模板?

在函数模板的情况下,用户可以使用任何类型调用该函数。这里相同的函数根据类型有几个版本。现在,在这种情况下,对于同一个函数,由于版本不同,必须维护vtable中的许多项。

模板都是关于编译器在编译时生成代码的。虚函数都是关于运行时系统确定在运行时调用哪个函数。

一旦运行时系统发现它需要调用模板化的虚函数,编译就完成了,编译器就不能再生成相应的实例了。因此,不能使用虚拟成员函数模板。

然而,结合多态性和模板产生了一些强大而有趣的技术,特别是所谓的类型擦除。

我看了所有的14个答案,有些有原因,为什么虚拟模板的功能不能工作,其他人显示了一个工作周围。一个答案甚至表明虚类可以有虚函数。这不足为奇。

我的回答将给出一个直接的理由,为什么标准不允许虚模板函数。因为很多人都在抱怨。首先,我不敢相信有人说虚函数可以在编译时推导出来。这是我听过的最蠢的话。

不管怎样。我确定标准规定指向对象的this指针是其成员函数的第一个参数。

struct MyClass
{
 void myFunction();
}

// translate to
void myFunction(MyClass*);

既然我们都清楚了。然后,我们需要知道模板的转换规则。模板化的参数在它可以隐式转换的内容上受到极大的限制。我不记得所有的内容,但是你可以查看c++ Primer以获得完整的参考。例如,T*可转换为const T*。数组可以转换为指针。但是,派生类不能作为模板形参转换为基类。

struct A {};
struct B : A {};

template<class T>
void myFunction(T&);

template<>
void myFunction<A>(A&) {}

int main()
{
 A a;
 B b;

 myFunction(a); //compiles perfectly
 myFunction((A&)b); // compiles nicely
 myFunction(b); //compiler error, use of undefined template function
}

我希望你们能明白我的意思。你不能使用虚拟模板函数,因为就编译器而言,它们是两个完全不同的函数;作为隐式参数,此参数具有不同的类型。

虚拟模板不能工作的另一个原因同样有效。因为虚表是快速实现虚函数的最佳方式。

虽然很多人已经回答了一个老问题,但我相信一个简洁的方法,与其他发布的方法没有太大不同,就是使用一个小宏来帮助减轻类声明的重复。

// abstract.h

// Simply define the types that each concrete class will use
#define IMPL_RENDER() \
    void render(int a, char *b) override { render_internal<char>(a, b); }   \
    void render(int a, short *b) override { render_internal<short>(a, b); } \
    // ...

class Renderable
{
public:
    // Then, once for each on the abstract
    virtual void render(int a, char *a) = 0;
    virtual void render(int a, short *b) = 0;
    // ...
};

现在,要实现我们的子类:

class Box : public Renderable
{
public:
    IMPL_RENDER() // Builds the functions we want

private:
    template<typename T>
    void render_internal(int a, T *b); // One spot for our logic
};

这样做的好处是,当添加一个新支持的类型时,它可以从抽象头文件中完成,而不必在多个源文件/头文件中进行修改。