4

Ошибка ниже меня сбивает с толку. Вот короткая часть гораздо более сложного кода. Мне кажется странным, что только существование шаблонного конструктора и виртуального метода вызывает ошибку и только при копировании инициализации объекта.Ошибка «рекурсивная на всех путях управления» при использовании конструктора копирования и присутствия виртуальной функции

У кого-нибудь есть идеи? Благодарю.

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; 
    } 

ответ

4

У вас есть неприятное слияние copy elision и конструктор, который делает копию параметра.

Во-первых, давайте проясним недоразумение: A a = c; является не эквивалентно A a; a = c;. Первый вызов копирует ctor, второй вызывает оператор присваивания. Посмотрите сами, используя this code sample.

Конструктор A::A<T>(T) мог бы сделать копию T всякий раз, когда он называется. К сожалению, если вы его назовете, используя параметр A (или в вашем примере C, который есть -A), параметр попытается скопировать сам, который вызывает снова A::A<T>(T), который копирует себя снова и снова ... до переполнения стека ,

Почему это не происходит, когда у вас нет virtual void f() в B? Это побочный эффект от копирования, который является функцией, зависящей от реализации. Наличие виртуального метода, возможно, было достаточно для визуальной студии, чтобы решить не удалять копию, но в любом случае вы не должны зависеть от нее. Вот почему вы strongly advised not to have observable side-effects for copy ctors.

На всякий случай, когда вы искали решение, вы можете удалить копию, сменив A::A<T>(T), чтобы получить ссылку, например A::A<T>(T&). Еще лучше, возьмите const T&, потому что это помогает гарантировать отсутствие побочных эффектов в ctor (так как вы не можете изменить T).

+0

Спасибо за полноту, действительно, мне нужно будет использовать ссылку. – user883041

+1

Технически это не * copy-constructor *, а * конструктор преобразования *. A * copy-constructor * принимает объект того же типа, что и источник, в то время как * конструктор преобразования * принимает объект другого типа. Конструктор шаблонов не может быть * copy-constructor *. –

+0

@ DavidRodríguez-dribeas спасибо. Точнее, только специализация этого конструктора шаблонов (где 'T' является' A') является конструктором копирования. – congusbongus

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

После этого рекурсии так, чтобы сделать копию, вам нужно сделать копию, вам нужно сделать копию .... :)

Для правильного использования см this.