如何在类模板中延迟对虚拟函数的调用?

编程语言 2026-07-09

请看下面的代码:

template <typename T>
int badToCall() = delete;

struct IA {
    virtual ~IA() {}
    virtual int foo() const = 0;
};

template <typename T>
struct A : public IA {
    virtual int foo() const override final { return badToCall<T>(); }
};

int main() { A<int> a; }

如何延迟对A<int>::foo的求值/实例化,以便我可以在这里实例化A<int>而不触发一个 "call to deleted function" 错误?

我在网上找到了类似的问题,其中有几个提到在实例化A时必须构造A的vtable,从而触发对A::foo 的急切求值。但在我看来,那只需要对A::foo进行声明,而不需要实际定义,类似于普通的非模板虚函数声明。因此,我觉得似乎有可能延迟对完整求值。

编辑:如果我不使用模板,我可以这样为int编写实现:

AInt.h

struct AInt: public IA {
  virtual int foo() const override final;
}

实现可以放在cpp中,与模板特化一起:

AInt.cpp

template<>
int badToCall<int>() {
  return 1;
}

int AInt::foo() {
  return badToCall<int>();
}

这个结构体在仅包含AInt.h时仍然可以被实例化;如下所示:

#include "AInt.h"

int main() {
  AInt a;
}

我想实现同样的效果,但不想手动提供一个 AInt 结构体,因此我希望能够这样做

int main() {
  A<int> a;
}

因此我想延迟定义 A<X>::foo,最好不需要列出显式的实例化。我猜我希望那些不使用 A<X>::foo 的编译单元不为它输出任何代码,而使用它的编译单元输出代码,并进行去重。

解决方案

将代码在头文件和.cpp/.inl之间尽量按常规代码分开。

不要在头文件中提供 A<T>::foo 的实现,而只在需要的地方提供

在A.hpp:

struct IA {
    virtual ~IA() {}
    virtual int foo() const = 0;
};

template <typename T>
struct A : public IA {
    virtual int foo() const override final;
};

在main.cpp

#include <A.hpp>
int main() { A<int> a; }

并且在Serialize.cpp中,大致如下:

#include <A.hpp>

template <typename T>
int badToCall() = delete;

// All specialization of `badToCall` for specific type.
template <>
int badToCall<A>() { return 42; }

template <typename T>
int A::foo() const { return badToCall<T>(); }

void Serialize(const A<int>& a)
{
    a.foo();
}

我会用重载来替代对specialization的使用

在A.inl / A.hxx或者关于内联实现的任何约定

#include <A.hpp>

struct ADL_enabler {}; // Needed mostly for built-in types.

template <typename T>
int A::foo() const { return foo_impl(std::type_identity<T>, ADL_enabler{}); }

// Each usage of `A::foo` should see declaration of matching overload `foo_impl`

以及在SerializeInt.cpp:

#include <A.hpp>
#include <A.inl>

int foo_impl(std::type_identity<int>, ADL_enabler) { return 42; }

void Serialize(const A<int>& a)
{
    a.foo();
}

以及在SerializeDouble.cpp:

#include <A.hpp>
#include <A.inl>

int foo_impl(std::type_identity<double>, ADL_enabler) { return 51; }

void Serialize(const A<double>& a)
{
    a.foo();
}
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章