使用复制构造函数和存在虚拟函数时出错"recursive on all control paths"

Error "recursive on all control paths" when copy constructor is used and virtual function present

本文关键字:recursive 出错 on all paths control 函数 复制 构造函数 虚拟 存在      更新时间:2023-10-16

下面的错误让我感到困惑。这是一个更复杂的代码的一小段。对我来说似乎很奇怪,只有模板化构造函数和虚拟方法的存在才会导致错误,并且仅在复制初始化对象时。

有人有想法吗?谢谢。

    class A
    {
      long *p;
    public:
      A():p(0)
      {
      }
      template<class T>
      A(T val):p(val)// 1
      {
      }
      operator long*()
      {
       return p;
      }
    };
    class B
    {
      virtual void f()// 2
      {
      }
    };
    class C : public A, public B
    {
    };
    void main()
    {
      C c;

main()中的下一行是

      A a=c; 

如果标记为 // 1// 2 的行都存在,这将触发以下错误:

warning C4717: 'C::C' : recursive on all control paths, function will cause runtime stack overflow 

但是当在main()中使用以下方法时,没有错误:

      A a;
      a=c;
    }

你所拥有的是复制 elision 和一个制作参数副本的构造函数的令人讨厌的汇合。

首先,让我们澄清一个误区:A a = c;等同于A a; a = c;。第一个调用复制 ctor,第二个调用赋值运算符。使用此代码示例亲自查看。

构造函数A::A<T>(T)可以在调用T时创建它的副本。不幸的是,如果您使用 A 参数调用它(或在您的示例 C 中,这是一个A (,该参数将尝试复制自身,这会再次调用A::A<T>(T),这会一次又一次地复制自身......直到堆栈溢出。

当您没有B virtual void f()时,为什么不会发生这种情况?这是复制 elision 的副作用,复制 elision 是一个依赖于实现的功能。拥有虚拟方法可能足以让Visual Studio决定不删除副本,但无论如何您都不应该依赖它。这就是为什么强烈建议您不要对复制 ctor 产生可观察到的副作用。

以防万一您正在寻找解决方案,您可以通过更改A::A<T>(T)来删除副本以获取引用,例如 A::A<T>(T&) .更好的是,采取const T&,因为这有助于确保 ctor 中没有副作用(因为您无法修改T (。

    A a=c; // this results in A::A(C c) template constructor instantiation.

之后是递归,因为要制作副本,您需要制作副本,您需要制作副本:)。

有关正确用法,请参阅此处。