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;
    }
jogojapan
  • 68,383
  • 11
  • 101
  • 131
user883041
  • 51
  • 1
  • 4

2 Answers2

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

Community
  • 1
  • 1
congusbongus
  • 13,359
  • 7
  • 71
  • 99
  • Thanks for completeness, indeed i should have to use a reference. – user883041 Apr 04 '13 at 02:06
  • 1
    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*. – David Rodríguez - dribeas Apr 04 '13 at 03:53
  • @DavidRodríguez-dribeas thanks. To be more precise, only the specialisation of that template constructor (where `T` is `A`) is a copy constructor. – congusbongus Apr 04 '13 at 04:02
  • 1
    There's no such thing as an *assignment constructor*. `A a; a = c;` calls the *default constructor* and the *assignment operator*. – Ben Voigt Apr 04 '13 at 04:21
  • 2
    @CongXu. No. A template constructor CANNOT be a copy constructor, no matter how specialized or what argument types. – Ben Voigt Apr 04 '13 at 04:22
  • @BenVoigt Thanks; I looked more into the matter and found [this](http://stackoverflow.com/questions/3527248/mi-and-implicit-copy-constructor-bug-was-under-what-conditions-can-a-template) which explains the issue in more detail. Looks like it's an MSVC bug? – congusbongus Apr 04 '13 at 04:26
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.

Arun
  • 2,087
  • 2
  • 20
  • 33