C++为什么没有虚的成员函数模板

对应知乎问题 https://www.zhihu.com/question/474773455 (opens new window)

标准已经规定 ref (opens new window): Function templates cannot be declared virtual,那么为什么这么规定呢?


首先,子类并不知道基类的成员函数模板有哪些实例,比如:

#include <memory>
#include <print>

struct Base {
    template <typename T>
    /* virtual */ void foo(T) {
        std::println("Base::foo");
    }
};

struct Derived : Base {
    template <typename T>
    void foo(T) {
        std::println("Derived::foo");
    }
};

int main() {
    std::unique_ptr<Base> d = std::make_unique<Derived>();
    d->foo(114514);  // Base::foo,怎么才能输出 Derived::foo?
}

想要上面代码只有基类实例化了 Base::foo<int>,子类没实例化。如果想要调 Derived::foo<int>,除非运行时调编译器给你实例化一个。(如果你觉得编译器足够聪明会尽可能实例化子类的函数模板,那再想想多编译单元 + 动态链接,编译器、链接器都无法拿到全部信息)


上面例子的原因是,基于继承的多态已经满足不了我们了。于是我们掏出非侵入式多态(往期有讲原理,但这里不需要知道原理),假设有个神奇的宏 INTERFACE:

#include <memory>
#include <print>

struct Base {
    template <typename T>
    /* virtual */ void foo(T) {
        std::println("Base::foo");
    }
};

struct Derived : Base {
    template <typename T>
    void foo(T) {
        std::println("Derived::foo");
    }
};

INTERFACE(Base2, Base, FUNC(foo, int));

int main() {
    std::unique_ptr<Base2> d = std::make_unique<Derived>();
    d->foo(114514);
}

区别就是 INTERFACE 宏指定了哪些函数模板会实例化,这样就完美解决了问题。我用微软的 proxy 库试了一下,是可以的:https://godbolt.org/z/b7q1z4WPW (opens new window)


这里甚至都没提到虚表的事情,因为只在语法层面探讨,没有到实现层面。虚表绑定了基于继承的多态,对于非侵入式多态,虚表不够用了。至于为什么可以看上面的知乎问题。(这可能是面试官想要的答案,但是,问题的根源是语法而不是实现)