Re: нюансы организации friend-методов
От: dead0k  
Дата: 02.06.15 16:08
Оценка: +1
Здравствуйте, _hum_, Вы писали:

__>Раньше думал, что френд-функция ничем по семантике использованию не отличается от френд-классов, а оказалось, для нее не проходят вещи, которые вроде бы должны были проходить. А именно, не компилируется код, наподобие следующего:


ideone.com (c++14):

prog.cpp:10:39: error: invalid use of incomplete type 'class CA'
friend void CA::LaunchProcess(void);
^
тобишь извольте CA определять целиком перед CI

ps/
это, в принципе, и логично. если у тебя там ядрена боньба, то давать кнопку можно только тем, кто уже известен, а то мало ли, включят хедер с одним только CI в юнит, где будет CA, написанный джихадистами.
Отредактировано 02.06.2015 16:11 dead0k . Предыдущая версия .
нюансы организации friend-методов
От: _hum_ Беларусь  
Дата: 02.06.15 15:23
Оценка:
Раньше думал, что френд-функция ничем по семантике использованию не отличается от френд-классов, а оказалось, для нее не проходят вещи, которые вроде бы должны были проходить. А именно, не компилируется код, наподобие следующего:
class CA;

////////////////////////////////
////          CI            ////
////////////////////////////////
class CI
{
    int m_i;

    friend void CA::LaunchProcess(void);
public:
    //----------------------
    int get_value(void)const
    {
        return m_i;
    }
    //----------------------
};
////////////////////////////////

////////////////////////////////
////          CA            ////
////////////////////////////////
class CA
{
    CI  m_ProcessResult;

    int m_j;
    //----------------------
    void LaunchProcess(void)
    {
        m_ProcessResult.m_i = 0;// Ошибка: error C2248 : невозможно обратиться к private член
    }
    //----------------------
    void ReactOnProcessResult(void)
    {
        if(m_ProcessResult.get_value() == 1)
        {
            //<initiate nuclear strike>
        };
    }
    //----------------------
    void Foo_1(void)
    {
        //---
        if(m_ProcessResult.get_value() == 1)
        {
            ++m_j;
        };
        //---
    }
    //----------------------
    void Foo_2(void);
    void Foo_3(void);
    //<....>

};
////////////////////////////////


На всякий случай, зачем мне это нужно. Пишу на микроконтроллере, потому приходится постоянно идти на компромисс между прозрачностью кода и быстродействием/объемом памяти. С другой стороны пишу систему, ошибки в работе которой могут дорого стоить. В связи с этим хочу хоть как-то себя обезопасить. В частности, стоит проблема в том, чтобы гарантировать, что данную переменную объекта может менять только заданный метод, но никак не другой. Например, указанный выше код есть попытка решения этой проблемы для случая, когда наивный код выглядел бы так:

///////////////////////////////
////          CA            ////
////////////////////////////////
class CA
{
    int m_i;

    int m_j;
    //----------------------
    void LaunchProcess(void)
    {
        m_i = 0;
    }
    //----------------------
    void ReactOnProcessResult(void)
    {
        if(m_i == 1)
        {
            //<initiate nuclear strike>
        };
    }
    //----------------------
    int Foo_1(void)
    {
        //---
        if(m_i == 1)//например, можно ошибиться и написать m_i = 1
        {
            ++m_j; //например, можно ошибиться и написать ++m_i; 
        };
        //---
        m_i = Foo_2(); // например, можно понадеяться, что Foo_2 всегда дает нуль, а потом забыть, и в Foo_2 начать писать нетривиальный код
    }
    //----------------------
    int Foo_2(void)
    {
        return 0; 
    }
    //----------------------
    int Foo_3(void);
    //<....>

};
////////////////////////////////


Заранее благодарю за конструктивные предложения.
Re: нюансы организации friend-методов
От: Abyx Россия  
Дата: 02.06.15 15:37
Оценка:
Здравствуйте, _hum_, Вы писали:

__> int get_value(void)const


В С++ не надо писать "(void)", не за чем.
In Zen We Trust
Re[2]: нюансы организации friend-методов
От: _hum_ Беларусь  
Дата: 02.06.15 16:21
Оценка:
Здравствуйте, dead0k, Вы писали:

D>Здравствуйте, _hum_, Вы писали:


__>>Раньше думал, что френд-функция ничем по семантике использованию не отличается от френд-классов, а оказалось, для нее не проходят вещи, которые вроде бы должны были проходить. А именно, не компилируется код, наподобие следующего:


D>ideone.com (c++14):


D>prog.cpp:10:39: error: invalid use of incomplete type 'class CA'

D> friend void CA::LaunchProcess(void);
D> ^
D>тобишь извольте CA определять целиком перед CI

D>ps/

D>это, в принципе, и логично. если у тебя там ядрена боньба, то давать кнопку можно только тем, кто уже известен, а то мало ли, включят хедер с одним только CI в юнит, где будет CA, написанный джихадистами.

это было бы логично, если бы не компилировался и код, где в качестве френд-методов выступают все методы класса CA (класс CA — дружественный):

////////////////////////////////
////          CI            ////
////////////////////////////////
class CI
{
    int m_i;

    friend class CA;
public:
    //----------------------
    int get_value(void)const
    {
        return m_i;
    }
    //----------------------
};
////////////////////////////////

////////////////////////////////
////          CA            ////
////////////////////////////////
class CA
{
    CI  m_ProcessResult;

    int m_j;
    //----------------------
    void LaunchProcess(void)
    {
        m_ProcessResult.m_i = 0;
    }
    //----------------------
    void ReactOnProcessResult(void)
    {
        if(m_ProcessResult.get_value() == 1)
        {
            //<initiate nuclear strike>
        };
    }
    //----------------------
    void Foo_1(void)
    {
        //---
        if(m_ProcessResult.get_value() == 1)
        {
            ++m_j;
        };
        //---
    }
    //----------------------
    void Foo_2(void);
    void Foo_3(void);
    //<....>

};
////////////////////////////////


Но он благополучно компилируется.
Re[3]: нюансы организации friend-методов
От: dead0k  
Дата: 02.06.15 16:36
Оценка:
Здравствуйте, _hum_, Вы писали:

__>Но он благополучно компилируется.

Логично.
Re[4]: нюансы организации friend-методов
От: _hum_ Беларусь  
Дата: 02.06.15 16:57
Оценка:
Здравствуйте, dead0k, Вы писали:

D>Здравствуйте, _hum_, Вы писали:


__>>Но он благополучно компилируется.

D>Логично.
Что логично? То, что компилируется, или то, что нелогично, когда одна и та же идеология (исключения для доступа к закрытым данным класса) используется по-разному для разных случаев (если разрешать всем методам — то все ок, а если только отдельным, то начинаются танцы с бубнами)
Re[5]: нюансы организации friend-методов
От: VTT http://vtt.to
Дата: 02.06.15 17:19
Оценка:
Здравствуйте, _hum_, Вы писали:

__>Здравствуйте, dead0k, Вы писали:


D>>Здравствуйте, _hum_, Вы писали:


__>>>Но он благополучно компилируется.

D>>Логично.
__>Что логично? То, что компилируется, или то, что нелогично, когда одна и та же идеология (исключения для доступа к закрытым данным класса) используется по-разному для разных случаев (если разрешать всем методам — то все ок, а если только отдельным, то начинаются танцы с бубнами)

Сдается мне, что все ваше недовольство сводится к невозможности сделать forward declaration (да еще и неявный) метода класса.
Написав friend class CA; вы одновременно делаете forward declaration класса CA.
А написав friend void CA::LaunchProcess(void); такой фокус уже не пройдет. Надо подключать заголовок полноценным объявлением CA.
Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.
Re[5]: нюансы организации friend-методов
От: Кодт Россия  
Дата: 02.06.15 17:37
Оценка:
Здравствуйте, _hum_, Вы писали:

__>Что логично? То, что компилируется, или то, что нелогично, когда одна и та же идеология (исключения для доступа к закрытым данным класса) используется по-разному для разных случаев (если разрешать всем методам — то все ок, а если только отдельным, то начинаются танцы с бубнами)


gcc 4.8 — 4.9 можно считать эталонным компилятором. Если он ругается на несоответствие стандарту, значит, так оно и есть.
Видимо, компилятор для целевой платформы более кривой. Он либо вообще содержит ошибки, поскольку проворонил ill-formed программу (friend void CA::LaunchProcess()), либо просто выдаёт корявую диагностику ("ах извините, я там на friend-объявлении промолчал, поэтому буду бить вас по пальцам в точках использования").
Перекуём баги на фичи!
Re: нюансы организации friend-методов
От: ArtDenis Россия  
Дата: 03.06.15 04:17
Оценка:
Здравствуйте, _hum_, Вы писали:

__>На всякий случай, зачем мне это нужно. Пишу на микроконтроллере, потому приходится постоянно идти на компромисс между прозрачностью кода и быстродействием/объемом памяти.


Современные компиляторы настолько хорошо оптимизирует, что использование friend-ов для прямого обращения к данным другого объекта не имеет смысла. Разбивай сущности на классы так как это требует предметная область и не парься по поводу быстродействия.
[ 🎯 Дартс-лига Уфы | 🌙 Программа для сложения астрофото ]
Re[6]: нюансы организации friend-методов
От: _hum_ Беларусь  
Дата: 03.06.15 10:21
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, _hum_, Вы писали:


__>>Что логично? То, что компилируется, или то, что нелогично, когда одна и та же идеология (исключения для доступа к закрытым данным класса) используется по-разному для разных случаев (если разрешать всем методам — то все ок, а если только отдельным, то начинаются танцы с бубнами)


К>gcc 4.8 — 4.9 можно считать эталонным компилятором. Если он ругается на несоответствие стандарту, значит, так оно и есть.

К>Видимо, компилятор для целевой платформы более кривой. Он либо вообще содержит ошибки, поскольку проворонил ill-formed программу (friend void CA::LaunchProcess()), либо просто выдаёт корявую диагностику ("ах извините, я там на friend-объявлении промолчал, поэтому буду бить вас по пальцам в точках использования").

Я не понимаю логики. Ведь объявление френд-функций по своей сути не совсем то же самое, что декларирование функций. Декларирование функции производится для генератора кода компилятора (компилятор = препроцессор + синтаксический анализатор + генератор кода), чтобы он мог строить код вызова этой функции еще до ее определения. Объявление же френд-функции — это просто указание синтаксическому анализатору, чтобы он не ругал программиста за то, что тот решил использовать в тексте программы код обращения к закрытым данным. Иными словами, это фича верхнего уровня (как того же спецификатора const) и на принципиальную возможность построения кода это не оказывает никакого влияния.
И второй момент — почему опережающее декларирование френд-вкласса допустимо, а френд-метода нет? С чем это связано? Почему

class A
{
    int m_i;

    friend class B; 
};



class B
{
    void f(A* pA){ pA->m_i = 0;}
};



можно, а идентичный по семантике

class A
{
    int m_i;

    friend void B::f(A*); 
};



class B
{
    void f(A* pA){ pA->m_i = 0;}
};


нет ?



Короче, пока решил проблему так:

class CA;

class CEvaluateProcessFunctor
{
    int m_i;
    CA* m_pA;
public:
    CEvaluateProcessFunctor(CA* pA);

    void operator()(void)
    {
        m_i = 0;
    }
    int GetResult(void)const{return m_i;}

};

////////////////////////////////
////          CA            ////
////////////////////////////////
class CA
{

    int m_j;

    CEvaluateProcessFunctor  m_OFEvaluateProcess;
    //----------------------
    void ReactOnProcessResult(void)
    {
        if(m_OFEvaluateProcess.GetResult() == 1)
        {
            //<initiate nuclear strike>
        };
    }
    //----------------------
    void Foo_1(void)
    {
        //---
        if(m_OFEvaluateProcess.GetResult() == 1)
        {
            ++m_j;
        };
        //---
    }
    //----------------------
    void Foo_2(void);
    void Foo_3(void);
    //<....>

};
////////////////////////////////
Отредактировано 03.06.2015 15:42 Кодт . Предыдущая версия .
Re[7]: нюансы организации friend-методов
От: Кодт Россия  
Дата: 03.06.15 15:54
Оценка:
Здравствуйте, _hum_, Вы писали:

__>Я не понимаю логики. Ведь объявление френд-функций по своей сути не совсем то же самое, что декларирование функций. Декларирование функции производится для генератора кода компилятора (компилятор = препроцессор + синтаксический анализатор + генератор кода), чтобы он мог строить код вызова этой функции еще до ее определения. Объявление же френд-функции — это просто указание синтаксическому анализатору, чтобы он не ругал программиста за то, что тот решил использовать в тексте программы код обращения к закрытым данным. Иными словами, это фича верхнего уровня (как того же спецификатора const) и на принципиальную возможность построения кода это не оказывает никакого влияния.


Френд-объявление это частный случай объявления.
Он, например, позволяет различать сигнатуры
class X {
  int t;
  friend void foo(int);
};

foo(int)  { X x; x.t = 0; } // можно
foo(char) { X x; x.t = 0; } // ошибка


__>И второй момент — почему опережающее декларирование френд-вкласса допустимо, а френд-метода нет? С чем это связано? Почему


Именно потому же, почему нельзя написать
class X;
void X::f();

class X { void f(); };

Синтаксис С++ не предусматривает возможности делать объявления членов не объявленного класса.
Объявления зависимых имён (членов и типов) должны первый раз встретиться внутри объявления класса.

Можно предположить, зачем сделано именно так. Например, чтобы различать статические функции и функции-члены.
class X;
void X::f();
void X::g();
void X::h();
int X::t;

void go() {
  X x;
  x.f(); // thiscall или cdecl? виртуальный или нет? (ну ладно, это можно через невиртуальную точку входа сделать)
  x.g(); // thiscall или cdecl? виртуальный или нет?
  X::h(); // можно или нельзя?
  X.t = 1; // какое смещение? нет ли там виртуального наследования? (что, опять через thunk-функцию делать?)
}

// где-то в другой единице трансляции

class Y {
  int t;
};

class X : virtual Y {
  static void f();
  virtual void g();
  void h(); // всё-таки, нельзя было...
  // мысленно родили thunk int& get_X_t(X& x) { return x.t; }
};


Конечно, можно в язык внести изменения, чтобы отдельно стоящее объявление члена несло исчерпывающую информацию. А зачем?

__>Короче, пока решил проблему так:


Непонятно, чем не понравилось сделать friend'ом весь класс CA?
Перекуём баги на фичи!
Re[8]: нюансы организации friend-методов
От: _hum_ Беларусь  
Дата: 04.06.15 10:10
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, _hum_, Вы писали:


__>>Я не понимаю логики. Ведь объявление френд-функций по своей сути не совсем то же самое, что декларирование функций. Декларирование функции производится для генератора кода компилятора (компилятор = препроцессор + синтаксический анализатор + генератор кода), чтобы он мог строить код вызова этой функции еще до ее определения. Объявление же френд-функции — это просто указание синтаксическому анализатору, чтобы он не ругал программиста за то, что тот решил использовать в тексте программы код обращения к закрытым данным. Иными словами, это фича верхнего уровня (как того же спецификатора const) и на принципиальную возможность построения кода это не оказывает никакого влияния.


К>Френд-объявление это частный случай объявления.

К>Он, например, позволяет различать сигнатуры
К>
К>class X {
К>  int t;
К>  friend void foo(int);
К>};

К>foo(int)  { X x; x.t = 0; } // можно
К>foo(char) { X x; x.t = 0; } // ошибка
К>


Это не показатель того, что френд-объявлени является декларированием. Скорее надо было бы привести такой пример:
class X {
  int t;
  friend void foo(int);
   
  int g(void){ foo(1);}
};

И да, действительно, он компилится,а значит, разработчики решили приравнять френд-обявление к декларированию. Зачем это было нужно делать, мне не очень понятно.

К>Синтаксис С++ не предусматривает возможности делать объявления членов не объявленного класса.

К>Объявления зависимых имён (членов и типов) должны первый раз встретиться внутри объявления класса.

К>Можно предположить, зачем сделано именно так. Например, чтобы различать статические функции и функции-члены.

К>
К>class X;
К>void X::f();
К>void X::g();
К>void X::h();
К>int X::t;

К>void go() {
К>  X x;
К>  x.f(); // thiscall или cdecl? виртуальный или нет? (ну ладно, это можно через невиртуальную точку входа сделать)
К>  x.g(); // thiscall или cdecl? виртуальный или нет?
К>  X::h(); // можно или нельзя?
К>  X.t = 1; // какое смещение? нет ли там виртуального наследования? (что, опять через thunk-функцию делать?)
К>}


Опять пример некорректный. На самом деле у вас бы было

class X;
void X::f();
void X::g();
void X::h();
int X::t;

void go() {
  X x; // Ошибка: создание объекта до определения класса
  <...>
}

А значит, либо пришлось класс определять, и тогда все ваши вопросы отпали бы, какая именно функция вызывается, либо не использовать соовтествующие вызовы.

К>Непонятно, чем не понравилось сделать friend'ом весь класс CA?


Сделать френдом весь класс CA де-факто означает, что соответствующая переменная становится доступной всем его методам. То есть, это ничем не отличается от примитивного варианта с обычной переменной-полем m_i в классе CA (со всеми описанными выше к нему опасностями), от которого я ставил цель избавиться.
Re[9]: нюансы организации friend-методов
От: Кодт Россия  
Дата: 04.06.15 10:57
Оценка:
Здравствуйте, _hum_, Вы писали:

__>Это не показатель того, что френд-объявлени является декларированием. Скорее надо было бы привести такой пример:

<>
__>И да, действительно, он компилится,а значит, разработчики решили приравнять френд-обявление к декларированию. Зачем это было нужно делать, мне не очень понятно.

Наследие.
Как в 98 году закрепили в стандарте существовавший на тот момент синтаксис, так оно и осталось.
Вообще, раздел стандарта про объявления, на мой взгляд, излишне обобщён. Чувствовалось желание минимизировать через унификацию. Но, что есть, то есть.

__>Опять пример некорректный. На самом деле у вас бы было

__>class X;
__>void go() {
__>  X x; // Ошибка: создание объекта до определения класса
__>}


Да не проблема!
class X;
.....

void go(X& x) { // нет никакой ошибки, - неполные классы можно передавать по ссылке и указателю
}


К>>Непонятно, чем не понравилось сделать friend'ом весь класс CA?


__>Сделать френдом весь класс CA де-факто означает, что соответствующая переменная становится доступной всем его методам. То есть, это ничем не отличается от примитивного варианта с обычной переменной-полем m_i в классе CA (со всеми описанными выше к нему опасностями), от которого я ставил цель избавиться.


Если не делать God Class, в котором вся-вся-вся логика программы засунута, то ничего ужасного в том, чтобы зафрендить его целиком. Ну будет доступ не из одного, а из трёх методов. Жалко, что ли?
Перекуём баги на фичи!
Re[10]: нюансы организации friend-методов
От: _hum_ Беларусь  
Дата: 04.06.15 11:46
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, _hum_, Вы писали:


__>>Сделать френдом весь класс CA де-факто означает, что соответствующая переменная становится доступной всем его методам. То есть, это ничем не отличается от примитивного варианта с обычной переменной-полем m_i в классе CA (со всеми описанными выше к нему опасностями), от которого я ставил цель избавиться.


К>Если не делать God Class, в котором вся-вся-вся логика программы засунута, то ничего ужасного в том, чтобы зафрендить его целиком. Ну будет доступ не из одного, а из трёх методов. Жалко, что ли?


Может, приведете наброски кода (о котором говорите) для моего примера выше, чтобы предметно обсуждать проблему?
Re[7]: нюансы организации friend-методов
От: MasterZiv СССР  
Дата: 04.06.15 11:50
Оценка:
Здравствуйте, _hum_, Вы писали:


__>И второй момент — почему опережающее декларирование френд-вкласса допустимо, а френд-метода нет? С чем это связано? Почему



Хотя бы потому, что не понятно, статический метод, или нет.
Re[11]: нюансы организации friend-методов
От: Кодт Россия  
Дата: 04.06.15 12:34
Оценка:
Здравствуйте, _hum_, Вы писали:

К>>Если не делать God Class, в котором вся-вся-вся логика программы засунута, то ничего ужасного в том, чтобы зафрендить его целиком. Ну будет доступ не из одного, а из трёх методов. Жалко, что ли?

__>Может, приведете наброски кода (о котором говорите) для моего примера выше, чтобы предметно обсуждать проблему?

Вот весь набросок кода
class CI
{
  friend class CA;
  int m_i;
public:
  int get_value() const { return m_i; }
};


Класс CA в том эскизе, который в первом сообщении темы, — не тянет на звание God Object. Ну да, пять методов, из которых реально доступ к полю CI::m_i нужен только одному.

Если есть возможность, разбей CA на ProcessLauncher и всё остальное.
Больше того: несуразностью выглядит дружба очень примитивного класса "некое целое число" с большим конкретным классом.
Тут напрашивается сделать по-другому
class ProcessLauncher
{
private:
  int m_ProcessResult;
protected: // для наследника
  int GetResult() const { return m_ProcessResult; }
  void Launch()
  {
    ..... m_ProcessResult = xxxx; .....
    ..... ReactOnProcessResult(); .....
  }

  // интерфейс наследника к предку
  virtual void ReactOnProcessResult() = 0;
};

class CA : protected ProcessLauncher
{
protected:
  void ReactOnProcessResult() override
  {
    ..... GetResult() .....
  }
public: // к пользователям
  void Foo1() {.....}
  void Foo2() {.....}
  void Foo3() {.....}
};


Если сильно жмёт виртуальность, — можно переделать, например, на static_cast (CRTP).
template<class TA> class ProcessLauncher
{
  .....
  void ReactOnProcessResult() { static_cast<TA*>(this)->ReactOnProcessResultImpl(); }
  .....
};
class CA : public ProcessLauncher<CA>
{
  friend class ProcessLauncher;
  void ReactOnProcessResultImpl() { ..... GetResult() ..... }
  .....
};
Перекуём баги на фичи!
Re[8]: нюансы организации friend-методов
От: _hum_ Беларусь  
Дата: 04.06.15 12:46
Оценка:
Здравствуйте, MasterZiv, Вы писали:

MZ>Здравствуйте, _hum_, Вы писали:



__>>И второй момент — почему опережающее декларирование френд-вкласса допустимо, а френд-метода нет? С чем это связано? Почему



MZ>Хотя бы потому, что не понятно, статический метод, или нет.


Если объявление френдом трактуется как прототипирование, то да. Я просто изначально полагал, что это не так, ибо, на мой взгляд, такой код
class X {
  int t;
  friend void foo(int);
   
  int g(void){ foo(1);}
};

создает какое-то извращенное представление о дружественности — получается, это функции, которые не только могут иметь доступ к методам и полям указанного класса, но еще и участвовать в определениях самих методов класса. Грубо говоря, это не реализация концепции доступа к методам и данным по ключу (коим является сигнатура функции), а некий вариант вырывания "с мясом" у класса отдельных методов.
Re[12]: нюансы организации friend-методов
От: _hum_ Беларусь  
Дата: 04.06.15 13:00
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, _hum_, Вы писали:


К>>>Если не делать God Class, в котором вся-вся-вся логика программы засунута, то ничего ужасного в том, чтобы зафрендить его целиком. Ну будет доступ не из одного, а из трёх методов. Жалко, что ли?

__>>Может, приведете наброски кода (о котором говорите) для моего примера выше, чтобы предметно обсуждать проблему?

К>Вот весь набросок кода

К>
К>class CI
К>{
К>  friend class CA;
К>  int m_i;
К>public:
К>  int get_value() const { return m_i; }
К>};
К>

К>
К>Класс CA в том эскизе, который в первом сообщении темы, — не тянет на звание God Object. Ну да, пять методов, из которых реально доступ к полю CI::m_i нужен только одному.

Ну и? Как было, так, фактически и осталось (по отношению к незащищенности от ошибок):
class CI
{
  friend class CA;
  int m_i;
public:
  int get_value() const { return m_i; }
};


///////////////////////////////
////          CA            ////
////////////////////////////////
class CA
{
    CI  m_I;

    int m_j;
    //----------------------
        void LaunchProcess(void)
    {
        m_I.m_i = 0;
    }
    //----------------------
    void ReactOnProcessResult(void)
    {
        if(m_I.m_i == 1)
        {
            //<initiate nuclear strike>
        };
    }
    //----------------------
    int Foo_1(void)
    {
        //---
        if(m_I.m_i == 1)//например, можно ошибиться и написать m_I.m_i = 1
        {
            ++m_j; 
        };
        //---
        m_I.m_i = Foo_2(); // например, можно понадеяться, что Foo_2 всегда дает нуль, а потом забыть, и в Foo_2 начать писать нетривиальный код
    }
    //----------------------
    int Foo_2(void)
    {
        return 0; 
    }
    //----------------------
    int Foo_3(void);
    //<....>

};
////////////////////////////////



К>Если есть возможность, разбей CA на ProcessLauncher и всё остальное.

Я же так и сделал выше, на что вы написали:

__>>Короче, пока решил проблему так:


К>Непонятно, чем не понравилось сделать friend'ом весь класс CA?



К>Тут напрашивается сделать по-другому


Не люблю я "техническое" наследование. Потому и вынес все в отдельный функтор.
Re[13]: нюансы организации friend-методов
От: Кодт Россия  
Дата: 04.06.15 13:11
Оценка:
Здравствуйте, _hum_, Вы писали:

__>Не люблю я "техническое" наследование. Потому и вынес все в отдельный функтор.


Дело вкуса. Декомпозировал на агрегат-сборку или на предка-наследника. Для эскизов выбор между ними не очевиден, хотя для реального кода может вообще один-единственный правильный вариант окажется.

С тотальным френдом и чистыми руками писанины было бы меньше. Но кое-кто боится ошибок, не знаю, насколько обоснованно.
Я бы просто сделал геттер и сеттер длинными_идентификаторами().
Перекуём баги на фичи!
Re: нюансы организации friend-методов
От: Myrgy Беларусь  
Дата: 08.06.15 16:10
Оценка:
Здравствуйте, _hum_

incpomplete тип, так как CI пока еще не знает о том, что в CA будет какой-то метод. а с friend class CA прокатывает, так как вы делаете брендом весь класс, соответственно допускается incomplete тип.
что бы решить проблему поменяйте местами классы и перенесите код методов в .cpp файлы

class CI;

class CA
{
public:
    void test(CI* ci);
};

class CI
{
  friend void CA::test(CI* ci);
  private: 
   int data;
};

// in .cpp
void CA::test(CI* ci)
{
    ci->data = 5;
}

int main() {
    // your code goes here
    return 0;
}
Отредактировано 08.06.2015 16:12 Myrgy . Предыдущая версия . Еще …
Отредактировано 08.06.2015 16:11 Myrgy . Предыдущая версия .
Re[2]: нюансы организации friend-методов
От: _hum_ Беларусь  
Дата: 09.06.15 11:23
Оценка:
Здравствуйте, Myrgy, Вы писали:

M>Здравствуйте, _hum_


M>incpomplete тип, так как CI пока еще не знает о том, что в CA будет какой-то метод. а с friend class CA прокатывает, так как вы делаете брендом весь класс, соответственно допускается incomplete тип.


вроде ж у же выяснили, что суть проблемы не в этом, а в том, что объявление френдом метода приравнивается к декларации (прототипированию) этого метода, а значит, предоставляет возможность его вызова еще до определения. а это не приводит к неоднозначности и противоречиям, только если до вызова (а значит, и до декларирования) уже будет определен класс, которому принадлежит данный метод. отсюда и требование — объявление френд-метода должно идти по тексту только после определения класса этого метода.

M>что бы решить проблему поменяйте местами классы и перенесите код методов в .cpp файлы


M>
M>class CI;

M>class CA
M>{
M>public:
M>    void test(CI* ci);
M>};

M>class CI
M>{
M>  friend void CA::test(CI* ci);
M>  private: 
M>   int data;
M>};

M>// in .cpp
M>void CA::test(CI* ci)
M>{
    ci->>data = 5;
M>}

M>int main() {
M>    // your code goes here
M>    return 0;
M>}
M>


этот вариант плох тем, что требует вынесение объекта типа CI за пределы класса CA (обратите внимание,в первоначально варианте значение результата инкапсулировано в сам объект класса CA)
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.