clang与gcc运行时的区别:用clang构建的c++类模板在没有复制构造函数的情况下崩溃,用复制构造函数构建的gcc

clang vs gcc run-time difference: c++ class template built w clang crashes w/o copy constructor, built w gcc crashes with copy constructor

本文关键字:构建 复制 clang 构造函数 gcc 崩溃 情况下 运行时 区别 c++      更新时间:2023-10-16

如果使用clang(而不是gcc)构建,除非我为TableTypeCarrier模板类包含一个复制构造函数,否则以下代码会在运行时崩溃吗?如果我包含复制构造函数,为什么在使用gcc构建时会遇到同样的运行时崩溃?

我定义了以下c++类:

class TableTypeCarrierBase {};
template<class T>
class TableTypeCarrier: public TableTypeCarrierBase {
public:
    TableTypeCarrier(const T * const p) : m_p(p) {}
#ifdef __clang__
    TableTypeCarrier(const TableTypeCarrier<T>& o) : m_p(o.m_p) {}
#endif
    const T * Get() const
    {
      return m_p;
    }
private:
    const T * const m_p;
};
struct PsiTable {
    PsiTable() : m_priv(NULL) { }
    template<typename T> void Set(const TableTypeCarrier<T> inT)
    {
      m_priv = &inT;
    }
    template<typename T> const T * Get() const
    {
      return (!m_priv) ? NULL : ((TableTypeCarrier<T>*)m_priv)->Get(); 
    }
private:
    const TableTypeCarrierBase *m_priv;
};

您会注意到TableTypeCarrier类模板的复制构造函数被条件化为只能由clang而不是gcc构建。

在最初编写这段代码时(没有上述条件化的复制构造函数),我只使用gcc来构建和测试它。当我尝试用clang构建它时,构建总是成功的,但在操作这些类的对象时会出现运行时崩溃。

我花了几个月的时间调试它。我不记得是什么路径导致我尝试向TableTypeCarrier模板类添加复制构造函数,但事实上,这解决了使用clang构建二进制文件的问题。不幸的是,简单地声明这个复制构造函数似乎会导致与最初描述的问题相同的问题,但只有在使用gcc构建时才会出现。

当然,我添加了预编译器指令,以便只有在使用clang构建时才对复制构造函数进行条件化。

这个修复对我有效,但我想了解原因。

如果你需要上下文来完全理解这个问题,这里有一个链接到在历史上这个确切的代码仍然存在的时候在整个项目中定义这些类的标题:

https://github.com/mkrufky/libdvbtee/blob/v0.4.0/libdvbtee/decode/table/table.h

此后,我重构了代码,并删除了TableTypeCarrierBase类和TableTypeCarrier模板类,因为它们是不必要的代理。尽管删除了这些代码,但我仍然想理解为什么当TableTypeCarrier有(或没有)复制构造函数时,用clang构建的二进制文件与用gcc构建的二进制代码的行为不同。

为什么clang构建的二进制文件需要这个复制构造函数?如果我包含gcc构建的二进制文件,为什么它会崩溃?

我在这里看到一个问题:

template<typename T> void Set(const TableTypeCarrier<T> inT)
{
  m_priv = &inT;
}

您通过值传递参数(并因此生成它的副本),但随后您获取它的地址并将其分配给成员变量。当函数结束时,您获取地址的对象将消失,留下一个悬空指针。

处于适当高警告级别的编译器应该对此进行标记。