clang's -Wweak-vtables是什么意思?

Bau*_*gen 71 c++ clang

我基本上不懂clang的-Wweak-vtables.这是我到目前为止所观察到的:

案例一:(触发警告)

class A {
    public:
    virtual ~A(){}        
};

class B : public A {
    public:
    virtual ~B(){}
};

int main(){}
Run Code Online (Sandbox Code Playgroud)

案例二:(不触发警告)

class A {
    public:
    virtual ~A(){}        
};   

int main(){}
Run Code Online (Sandbox Code Playgroud)

案例三:(不触发警告)

class A {
    public:
    virtual ~A();

};

A::~A(){}

class B : public A {
    public:
    virtual ~B(){}
};

int main(){}
Run Code Online (Sandbox Code Playgroud)

案例四:(触发警告)

class A {
    public:
    virtual ~A(){}
    virtual void fun(){}        
};    

class B : public A {
    public:
    virtual ~B(){}
};

int main(){}
Run Code Online (Sandbox Code Playgroud)

案例五:(不触发警告)

class A {
    public:
    virtual ~A(){}
    virtual void fun();      
};    

class B : public A {
    public:
    virtual ~B(){}
};

int main(){}
Run Code Online (Sandbox Code Playgroud)

案例六:(不触发警告)

class A {
    public:
    virtual ~A(){}
    virtual void fun(){}
};    

class B : public A {};

int main(){}
Run Code Online (Sandbox Code Playgroud)

案例七:(不触发警告)

class A {
    public:
    virtual ~A(){}
    virtual void fun(){}
};    

class B : public A {
    public:
    virtual void fun(){}
};

int main(){}
Run Code Online (Sandbox Code Playgroud)

确切的警告是

warning: 'A' has no out-of-line virtual method definitions; its vtable 
will be emitted in every translation unit [-Wweak-vtables]
Run Code Online (Sandbox Code Playgroud)

显然,如果我没有在类中声明非内联虚函数,那么当且仅当我从它派生并且派生类具有虚拟析构函数时,它才会导致某种问题.

问题:

  1. 为什么这是个问题?
  2. 为什么通过声明虚函数来解决这个问题?(警告说明定义)
  3. 为什么在我不从班级派生时不会发出警告?
  4. 当派生类没有虚拟析构函数时,为什么不发生警告?

Jef*_*tin 96

如果所有类的virtual方法都是内联的,则编译器无法选择在其中放置vtable的单个共享副本的转换单元 - 而是必须将vtable的副本放在需要它的每个目标文件中.在许多平台上,链接器能够通过丢弃重复定义或将所有引用映射到一个副本来统一这些多个副本,因此这只是一个警告.

在线外实现virtual函数使编译器能够选择实现该外联方法的转换单元作为类的实现细节的"主页",并将vtable的单个共享副本放在同一个转换单元中.如果多个方法是脱节的,编译器可以选择任意方法,只要该选择仅由类的声明确定; 例如,GCC在声明顺序中选择第一个非内联方法.

如果不覆盖类的任何方法,则virtual关键字没有可观察的效果,因此编译器不需要为类发出vtable.如果你没有派生A,或者你没有声明派生类的析构函数virtual,那么就没有重写的方法A,因此A省略了vtable.如果您声明一个额外的外联virtual方法来抑制警告并执行覆盖方法的内容A,则virtual需要在链接的翻译单元中提供非内联(及其随附的vtable副本)的实现否则链接将失败,因为缺少vtable.

  • 当同一类的(不同的)外联方法出现在几个不同的TU中时,它会怎么做? (2认同)
  • @Matt,基于类的声明,以一些任意但确定的方式选择一个外联方法,并且vtable最终与方法实现在相同的TU中.所有相关的TU都看到相同的声明:bar不一致的声明shenanigans,他们将同意所选择的方法,因此集体发出一个vtable. (2认同)
  • @JeffreyHantin你是什么意思*"B的析构函数不是虚拟的"*?编译器生成(隐式声明)`B`的析构函数应该是虚拟的(因为基类的析构函数已被声明为`virtual`). (2认同)
  • @Leon,quoth the spec:"在使用odr-used(3.2)或在第一次声明后明确默认为默认值时,默认定义为未删除的析构函数是隐式定义的." 情况6或7中没有任何内容使用或显式默认`~B()`,因此编译器不会隐式实例化默认定义.如果它不存在,它怎么可能是虚拟的?这些例子的微不足道造成了一些违反直觉的影响. (2认同)