【问题标题】:Do Explicit Instantiations of C++ Class Templates Instantiate Dependent Base Classes?C++ 类模板的显式实例化是否实例化依赖的基类?
【发布时间】:2010-09-13 22:51:58
【问题描述】:

我认为显式实例化请求也会自动实例化所有基类成员,但在使用 Visual Studio 2008 或 2010 构建此代码时,我得到了 linker error: unresolved external symbol "public: void Base<int>::foo(int)"

请注意,在bar() 中添加对foo() 的调用会强制编译器实例化Base<int>::bar() 并且构建成功,因此编译器似乎拥有实例化foo() 所需的所有信息。

显然,在 source.cpp 中显式实例化 Base<int> 可以使构建成功,但在显式实例化派生类时需要显式实例化任何依赖基类似乎很愚蠢。

这正常吗?我找不到标准关于这个问题的说法。

header.h

template<typename T>
class Base {
public:
    void foo();
};

template<typename T>
class Derived : public Base<T> {
public:
    void bar();
};

source.cpp

#include "header.h"

template<typename T>
void Base<T>::foo() { }

template<typename T>
void Derived<T>::bar() {
    // this->foo();   // adding this forces instantiation of foo()???
}

template class Derived<int>;

ma​​in.cpp

#include "header.h"

int main() {
    Derived<int> d;
    d.foo(); // Linker Error: unresolved external symbol "public: void Base<int>::foo(int)"
}

编辑:

看起来标准说只有类的成员才能通过显式类实例化来实例化,所以在我的示例中链接器错误是合理的。

注意一个类是由class-head { member-specification }定义的,“类定义中的成员规范声明了类的全部成员;不能添加任何成员别处。”所以成员只在大括号 { } 之间,公共基类成员不会成为派生类的成员,它们只能从派生类或派生类的对象中访问。

我剩下的唯一问题是为什么标准规定类模板的显式实例化只实例化成员而不是基类的成员?我的猜测是,这可以更好地控制在何处显式实例化的内容。使用显式模板类实例化的人很可能会将基类定义与派生类定义放在不同的文件中,并且会分别显式实例化每个。

【问题讨论】:

  • 是否为基类的派生触发器实例化添加显式构造函数?我怀疑编译器试图不做不必要的工作。拥有一个构造函数可能会让它意识到它也需要构造 Base,这可能会触发它。另外,你试过 gcc 或其他编译器吗?
  • 添加带或不带参数以及带或不带关键字“explicit”的构造函数不会触发基类 foo() 的实例化,但会触发派生成员 bar() 对 foo() 的调用隐式实例化。

标签: c++ templates


【解决方案1】:

标准说

类模板特化的显式实例化意味着其所有成员的实例化,该成员先前未在包含显式实例化的翻译单元中显式特化。

换句话说,它不要求基类依次显式实例化。这将导致它们的隐式实例化,而不会预先实例化它们的成员定义。标准中的某些文本说“成员”是指“直接”还是“继承”成员时,标准中有一些丑陋的故障,因为这对于编写标准措辞的人来说似乎是“显而易见的”,但对于读它的人。 C++0x 增加了一些说明(它也有 C++03 所没有的显式实例化声明定义之间的区别,但即使忽略了这一点, C++0x 的措辞包含更多的洞察力):

命名类模板特化的显式实例化也是 每个成员(不包括从基类继承的成员)的相同类型(声明或定义) classes) 之前没有在包含显式的翻译单元中显式特化 实例化,除非如下所述。 [注意:此外,它通常是显式实例化 关于类的某些依赖于实现的数据。 ——尾注]

【讨论】:

  • +1。他们是否无法做出更广泛的概括性声明,例如它与类及其所有静态成员的定义/声明无法区分?不管 vtable 是否被实例化,让它保持打开状态是他们承认的丑陋。
  • +1 该标准使用“类模板特化”来表示由“显式实例化”与使用模板的“显式特化”产生的实体使我感到困惑。我想不实例化基成员可以更好地控制实例化的内容和去向。基成员可以在与派生类不同的源文件中实例化。
  • @Potatoswatter:我们在派生类的实例化过程中,不是只需要声明基类来确定派生类的vtable的大小,其vtable中的值由链接器填写吗? ?如果任何被调用的函数没有在某个翻译单元中实例化,则会出现链接器错误。
  • @John:问题在于显式实例化是否认为 vtable 已使用。在大多数情况下,具有 vtable 的类具有虚拟析构函数,它使用 vtable 并强制它成为该翻译单元中的弱定义符号。一个更好的例子是typeid 运算符返回的type_info 对象。在构建库的情况下,您希望在显式实例化时定义所有辅助支持结构。
  • @Potatoswatter 没有标准的 vtable 这样的东西,因此标准不需要提及它。标准所知道的只是一个具有虚成员函数并被实例化的类。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多