clang vs gcc run-time 区别:c++ class 模板构建 w clang 崩溃 w/o 复制构造函数,构建 w 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)构建时,以下代码是否会在 run-time 期间崩溃,除非我包含 TableTypeCarrier 模板 class 的复制构造函数?如果我包含该复制构造函数,为什么我在使用 gcc 构建时会遇到相同的 run-time 崩溃?

我定义了以下 C++ classes:

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 class 模板的复制构造函数被条件化为仅由 clang 而不是 gcc 构建。

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

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

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

此修复对我有用,但我想了解原因。

如果您需要上下文来完全理解问题,这里有一个 link 到 header 定义了整个项目中的这些 classes 在历史上的某个时间点确切的代码仍然存在:

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

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

为什么 clang-built 二进制文件需要这个拷贝构造函数?如果包含 gcc-built 二进制文件,为什么它会崩溃?

我在这里看到一个问题:

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

您正在按值传递参数(并因此制作了它的副本),但是您正在获取它的地址并将其分配给成员变量。当函数结束时,您获取地址的对象消失,留下悬空指针。

处于适当高警告级别的编译器应该标记这一点。