Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

The error below is confusing me. Here is a short piece of a much more complicated code. It appears strange to me, that only the existence of both a templated constructor and a virtual method cause an error, and only when copy-initializing an object.

Does anyone have an idea? Thanks.

    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;

The next line in main() is

      A a=c; 

and this triggers the error below if both the lines marked // 1 and // 2 are present:

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

But when the following is used in main(), there is no error:

      A a;
      a=c;
    }
like image 275
user883041 Avatar asked Apr 04 '13 00:04

user883041


2 Answers

What you have is a nasty confluence of copy elision and a constructor that makes a copy of the parameter.

First, let's clear up a misunderstanding: A a = c; is not equivalent to A a; a = c;. The first calls the copy ctor, the second calls the assignment operator. See for yourself using this code sample.

The constructor A::A<T>(T) could make a copy of T whenever it is called. Unfortunately, if you call it using an A parameter (or in your example C, which is-a A), the parameter will attempt to copy itself, which calls A::A<T>(T) again, which copies itself again, and again... until stack overflow.

Why doesn't this happen when you don't have the virtual void f() in B? This is a side effect of copy elision, which is an implementation-dependent feature. Having the virtual method there might have been enough for visual studio to decide not to elide the copy, but in any case you shouldn't depend on it. This is why you are strongly advised not to have observable side-effects for copy ctors.

Just in case you were looking for a solution, you can remove the copy by changing A::A<T>(T) to take a reference, like A::A<T>(T&). Even better, take a const T& because this helps ensure that there are no side effects in the ctor (as you can't modify the T).

like image 148
congusbongus Avatar answered Nov 03 '22 01:11

congusbongus


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

After that it is recursion since to make a copy, you need to make a copy, you need to make a copy.... :)

For proper usage refer this.

like image 28
Arun Avatar answered Nov 02 '22 23:11

Arun