【问题标题】:Child class will not call inherited function from template base class子类不会调用从模板基类继承的函数
【发布时间】:2021-12-20 19:00:36
【问题描述】:

下面是我的代码。我删除了与问题无关的内容以保持简洁:

IChip.hpp(根抽象类)

class IChip {
    public:
    virtual bool test() noexcept = 0;
};

IMemory.hpp(包含所有内存通用的一些功能的摘要,读/写用于说明)

#include "IChip.hpp"

template <typename TAddr, typename TWord>
class IMemory: public IChip {
    protected:
    ...

    public:
    virtual TWord read(const TAddr addr) const noexcept = 0;
    virtual void write(const TAddr addr, const TWord data) const noexcept = 0;
    ...
    bool test() noexcept final override;
};

IMemory.cpp

#include "IMemory.hpp"

template <typename TAddr, typename TWord>
bool IMemory<TAddr, TWord>::test() noexcept {
    std::cout << "IMemory:test()" << std::endl;
    ...
    return true;
}

// Explicit instantiation. (Actually resides in a separate file IMemory.impl.cpp, but included here for clarity)
template class IMemory<uint16_t, uint8_t>;

HM62256.hpp(为了说明再次读/写)

#include "IMemory.hpp"

class HM62256: public IMemory<uint16_t, uint8_t> {
    private:
    ...

    public:
    ...
    uint8_t read(uint16_t addr) const noexcept final override;
    void write(uint16_t addr, uint8_t data) const noexcept final override;
    ...
};

HM62256.cpp

#include "HM62256.hpp"

uint8_t HM62256::read(uint16_t addr) const noexcept {
    uint8_t result = 0;
    ...
    return result;
}

void HM62256::write(uint16_t addr, uint8_t data) const noexcept {
    ...
}

main.cpp

int main(int argc, char *argv[]) {
    const uint8_t ADDR_PINS[] = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18};
    const uint8_t DATA_PINS[] = {19, 20, 21, 22, 23, 24, 25, 26};
    HM62256 *device = new HM62256(ADDR_PINS, DATA_PINS, 2, 3, 27);

    device->test();    // (1)

    delete device;
}

我的主要问题如下:

  • 此代码编译时没有问题(甚至没有警告)。
  • 我的期望是 (1) 处的 device-&gt;test() 将执行 HM62256IMemory 继承的 test() 方法。因此它应该将 IMemory:test() 打印到控制台。它不是。我在上述test() 方法上设置了断点,但调试器没有命中它们。相反,执行只是简单地进行,没有任何内容打印到控制台。我在delete device; 上设置了一个断点,它确实命中了它。

我的第二个问题是,由于代码编译得很好,(1) 处的函数调用必须调用一些东西,对吗? 什么它叫什么?

附加信息:

  • 编译器:针对 arm-linux 和 C++20 的 GCCC 8.3.0
  • 使用 CMake

【问题讨论】:

  • 这能回答你的问题吗? Why can templates only be implemented in the header file?IMemory.cpp中的代码需要在头文件中。
  • @RichardCritten 在 IMemory.cpp 中有一个明确的实例化,所以至少从这个角度来看,代码是好的。
  • 使用调试器进入test的调用,看看你的结果。
  • @S.Saad 由于 Godbolt 无法执行 ARM 代码,请参阅“执行代码”程序集上方的“输出”选项卡选项。那里的代码是正确的,它也必须在 ARM 上工作。你的问题在别的地方。也许尝试从头开始重新编译所有内容?

标签: c++ templates inheritance


【解决方案1】:

因此,正如 @Quimby 在 cmets 中所建议的那样,我的问题出在我的代码中的其他地方。

我是如何发现问题的:

  • 为编译器和链接器启用了一些清理标志。这让编译器抱怨我做错了一些事情,其中​​一些可能是我问题的根源
add_compile_options(-fsanitize=address)
add_link_options(-fsanitize=address -static-libasan)

我的问题是什么:

坦率地说,我不知道。最有可能的是,某些东西正在破坏 vtable。不过我有几个嫌疑人:

  • 我在 IMemory 的成员函数内增加了一个 const 变量,编译器在添加清理标志之前没有抱怨。
  • 我正在使用 C++20 中的 formatting 库。再次,编译器在添加清理标志之前没有抱怨,但在抱怨它找不到该包含之后。这让我相信要么是工具链本身坏了,要么是我安装的工具链坏了。
  • 我在IMemory 中有几个函数,在声明和定义中都有默认参数。添加清理标志后,编译器抱怨了这一点。解决方法是将参数的默认值仅放在声明中。

修复这三个后,编译器愉快地编译了代码,并且出现了预期的结果。请注意,我必须静态链接 libasan 才能使清理标志起作用。

【讨论】:

  • 很高兴看到您的问题得到解决。奇怪的是,只有第一个是实际问题,但它应该是编译器错误,除非你把 const 扔掉了。 GCC 还不支持std::format,不知道你从哪里得到的?参数再次应该是编译器错误,而不是运行时错误。诡异的。无论如何,我可以认真推荐对所有调试版本使用消毒剂,这是非常宝贵的工具。 undefinedaddressleakthread 很棒,如果你有 clang,memory 基本上会取代 Valgrind。
  • @Quimby 谢谢你并及时注意到。我没有骗你,gcc 之前愉快地编译了我有缺陷的代码,没有问任何问题。直到我添加了消毒剂,它才开始对我大喊大叫。我不知道发生了什么,就像你说的,其中一些问题应该是编译时错误,不知道为什么 gcc 没有捕捉到它们。此外,对于格式,它实际上确实编译并且之前确实输出了正确的格式。我在一个默认的 rasbpian 图像上,所以我想他们可能默认安装了 {fmt} 库。否则,我不知道发生了什么。
猜你喜欢
  • 2021-04-20
  • 2014-06-19
  • 1970-01-01
  • 1970-01-01
  • 2021-07-26
  • 2013-03-25
  • 2018-05-23
  • 2020-01-23
相关资源
最近更新 更多