[c++] использование классов во время их объявления
T *x
иначе получается, что оба класса требуют, чтобы каждый из них был полностью задефайнен. замкнутый круг.
иначе получается, что оба класса требуют, чтобы каждый из них был полностью задефайнен. замкнутый круг.
Если да, то как можно выкрутиться, если нужно описать структуру данных, ссылающуюся на себя.struct S
{
S* pS;
};
Или тебе нужно что-то особенное?
Жесть какая-то. Зачем так сложно?
Наверняка можно как-то проще сделать.
У тебя значения констант меняться должны в разных классах или что? Если да, то вот такой код компилиться, хотя на мой взгля .... ладно промолчу.
У меня вот так скомпилилось:
template <typename T> class A
{
public:
enum { N = 1 } asd;
T x;
};
struct B;
class A<B>
{
enum { N = 7 };
};
struct B
{
int x;
enum { M = A<B>::N };
};
Наверняка можно как-то проще сделать.У тебя значения констант меняться должны в разных классах или что? Если да, то вот такой код компилиться, хотя на мой взгля .... ладно промолчу.
У меня вот так скомпилилось:
template <typename T> class A
{
public:
enum { N = 1 } asd;
T x;
};
struct B;
class A<B>
{
enum { N = 7 };
};
struct B
{
int x;
enum { M = A<B>::N };
};
а нужного эффекта такой код не даст?


class B {
public:
int x;
enum { M = A<char>::N };
};
олько А(В*)
или никак
или никак
Если заведомо нет зависимости от типа (как в данном случае то вполне сойдет и вариант -а. Если зависимость есть (или возможна то получается цикл, который компилятор не разрулит (значение M зависит от N, N зависит от B, а B содержит M, то есть M зависит от самого себя). предложил вариант, в котором этот цикл разрулен вручную (то есть для A<B> фактически шаблон не используется но при этом внутрь класса A<B> нельзя поместить строчку B x; как сделано выше для шаблона (хотя можно поставить B* x, но это уже будет другой класс); это плохо, потому что тогда класс A<B> существует, но не является аналогом других классов с шаблоном A<>.
Вообще говоря, подобные циклические конструкции весьма сложны для анализа и отладки, и, имхо, лучше их избегать. То есть желательно, чтобы либо A ничего не знал о B (как у либо B ничего не знал об A.
Вообще говоря, подобные циклические конструкции весьма сложны для анализа и отладки, и, имхо, лучше их избегать. То есть желательно, чтобы либо A ничего не знал о B (как у либо B ничего не знал об A.
Спасибо всем ответившим!
Зависимость здесь по существу, попытаюсь объяснить подробнее.
Есть некий класс A, который содержит данные и указатель на какие-то другие елементы класса A, по типу списка, дерева. Например так:
тут все понятно - работает без проблем.
Далее - A* меняется на продвинутые (smart) указатели, например что-нибудь в духе
Такое тоже более-менее работает. Допустим shared_ptr реализуется так:
Что мы видим: shared_ptr<T> использует RCBase<T>, RCBase<T> использует T, A использует shared_ptr<A> --- то есть получается цикл.
Я привел пример с enum, потому что он у меня не компилися. Ладно, допустим c enum-ами я как-нибудь разобрался, фиг с ним. В приведенном примере все равно получается цикл. Причем цикл по существу, я не вижу нормальных способов его устранить. Соответственно, вопрос: как надо писать классы A, shared_ptr, RCBase, чтобы вся эта конструкция скомпилилась? На примере enum я понял, что как попало писать нельзя, надо аккуратно. Какие еще есть ограничения?
Зависимость здесь по существу, попытаюсь объяснить подробнее.
Есть некий класс A, который содержит данные и указатель на какие-то другие елементы класса A, по типу списка, дерева. Например так:
class A
{
int data;
A* left;
A* right
};
тут все понятно - работает без проблем.
Далее - A* меняется на продвинутые (smart) указатели, например что-нибудь в духе
class A
{
int data;
shared_ptr<A> left;
shared_ptr<A> right;
}
Такое тоже более-менее работает. Допустим shared_ptr реализуется так:
template <typename T> class RCBase
{
public:
int refcount;
T data;
};
template <typename T> class shared_ptr
{
private:
RCBase<T> *ptr;
public:
...
shared_ptr& operator = (constr shared_ptr& b) {
RCBase<T> old = ptr;
ptr = b.ptr;
if (ptr) ptr->refcount++;
if (old) old->refcount--;
}
// не буду вдаваться в детали, здесь это неважно
}
Что мы видим: shared_ptr<T> использует RCBase<T>, RCBase<T> использует T, A использует shared_ptr<A> --- то есть получается цикл.
Я привел пример с enum, потому что он у меня не компилися. Ладно, допустим c enum-ами я как-нибудь разобрался, фиг с ним. В приведенном примере все равно получается цикл. Причем цикл по существу, я не вижу нормальных способов его устранить. Соответственно, вопрос: как надо писать классы A, shared_ptr, RCBase, чтобы вся эта конструкция скомпилилась? На примере enum я понял, что как попало писать нельзя, надо аккуратно. Какие еще есть ограничения?
Здесь ты присваиваешь объекту указатель на этот объект
old тоже должен быть
RCBase<T>* old
и вроде пока всё нормально)
shared_ptr& operator = (constr shared_ptr& b) {
RCBase<T> old = ptr;
old тоже должен быть
RCBase<T>* old
и вроде пока всё нормально)
А каким компилятором у тебя это компилится?
как-то странно выглядит
class A<B> {
};
как-то странно выглядит
class A<B> {
};
Borland'овский компилятор, в Builder C++
Вообще, должно работать на других тоже - я не раз встречал такое объявление.
Вообще, должно работать на других тоже - я не раз встречал такое объявление.
А какое имя у описанного класса?
> old тоже должен быть
> RCBase<T>* old
Да, здесь я опечатался, имелось ввиду *
> и вроде пока всё нормально)
В таком виде как я написал --- компилится. Но тут есть два вопроса:
1) Это компилится конкретно моим компилятором. Насколько это переносимо? Будет ли это компилится другими компиляторами? Насколько это соотвествует стандарту?
2) То, что я описал --- это в некотором роде скелет, каркас. Простой пример, показывающий, как циклические зависимости между классами иногда действительно нужны. В реальности, в программе должна быть еще куча разных содержательных методов. И в них как раз не понятно, что можно использовать, а что нельзя, чтобы случайно не нарваться на какой-нибудь 'Incomplete type ...'
> RCBase<T>* old
Да, здесь я опечатался, имелось ввиду *
> и вроде пока всё нормально)
В таком виде как я написал --- компилится. Но тут есть два вопроса:
1) Это компилится конкретно моим компилятором. Насколько это переносимо? Будет ли это компилится другими компиляторами? Насколько это соотвествует стандарту?
2) То, что я описал --- это в некотором роде скелет, каркас. Простой пример, показывающий, как циклические зависимости между классами иногда действительно нужны. В реальности, в программе должна быть еще куча разных содержательных методов. И в них как раз не понятно, что можно использовать, а что нельзя, чтобы случайно не нарваться на какой-нибудь 'Incomplete type ...'
Не, в таком виде, как ты написал - у меня не компилится в восьмой студии, ну точней не "инстантинируется")
Думаю, что всёже не так-то и просто пример такой хитрой зависимости привести, чтоб реально нужно было, хотя вопрос, конечно, и интересный)
c:\documents and settings\dmitry\my documents\visual studio 2005\projects\cmasmastest\cmasmastest.cpp(22) : error C2440: 'initializing' : cannot convert from 'RCBase<T> *' to 'RCBase<T>'
class A obj1, obj2;
obj1 = obj2;
Думаю, что всёже не так-то и просто пример такой хитрой зависимости привести, чтоб реально нужно было, хотя вопрос, конечно, и интересный)
А какое имя у описанного класса?A<B>
1) Если не ошибаюсь, вполне соответствует.
2) Важно, чтобы все конструкции, которые расположены в объявлении класса shared_ptr вне его методов, не использовали класс A непосредственно, а только через указатель, то есть не привязывались к его структуре. Внутри методов (в том числе inline) этот указатель уже можно разыменовывать, так как при этом циклической зависимости не возникает.
P.S. Небольшой оффтопик: не стоит ли RCBase описать внутри класса shared_ptr?
2) Важно, чтобы все конструкции, которые расположены в объявлении класса shared_ptr вне его методов, не использовали класс A непосредственно, а только через указатель, то есть не привязывались к его структуре. Внутри методов (в том числе inline) этот указатель уже можно разыменовывать, так как при этом циклической зависимости не возникает.
P.S. Небольшой оффтопик: не стоит ли RCBase описать внутри класса shared_ptr?
Оставить комментарий
Landstreicher
Насколько разрешен сабж?Например, у меня не компилится следующая программа:
Компилятор gcc 4.0, 4.1.
Соответствует ли такое поведение стандарту? Если да, то как можно выкрутиться, если нужно описать структуру данных, ссылающуюся на себя.