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

2

5

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;
    }
Lupulin answered 4/4, 2013 at 0:12 Comment(0)
R
5

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).

Red answered 4/4, 2013 at 0:56 Comment(6)
Thanks for completeness, indeed i should have to use a reference.Lupulin
Technically it is not a copy-constructor, but a conversion-constructor. A copy-constructor takes an object of the same type as the source, while a conversion-constructor takes an object of a different type. A template constructor cannot be a copy-constructor.Waligore
@DavidRodríguez-dribeas thanks. To be more precise, only the specialisation of that template constructor (where T is A) is a copy constructor.Red
There's no such thing as an assignment constructor. A a; a = c; calls the default constructor and the assignment operator.Forereach
@CongXu. No. A template constructor CANNOT be a copy constructor, no matter how specialized or what argument types.Forereach
@BenVoigt Thanks; I looked more into the matter and found this which explains the issue in more detail. Looks like it's an MSVC bug?Red
S
2
    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.

Singlestick answered 4/4, 2013 at 1:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.