Re[12]: Почему нельзя предварительно объявить дружественную функ
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 26.09.23 16:17
Оценка:
Здравствуйте, so5team, Вы писали:

S>Для того, чтобы было вами подтверждено решение есть.


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

EM>>какая печаль с того, что прототип придется указывать со всеми атрибутами?


S>Такая, что мы начинаем делать частичное описание класса.


Еще раз: компилятору нет нужды делать для такого случая описание класса. Ему необходимо любым способом запомнить сигнатуру функции, объявленной во friend. Он может вообще не рассматривать квалификатор области видимости, как имя класса, а считать его именем произвольной области (в том числе namespace), которая станет известна позже.

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

S>Вы много херни говорите.


Вы, когда Вас заедает на каком-то принципиальном вопросе, говорите не меньше.

ЕМ>>С чего бы вдруг

S>>> friend void B::f(int a) const;
ЕМ>>стало достаточным для
S>>> b.f(0);
ЕМ>>?

S>Да потому, что это согласуется вот с таким, например (цинк)


С таким оно не согласуется, как верно отметил watchmaker, поскольку у функций-членов могут быть дополнительные атрибуты, потребные для формирования кода вызова.

Вообще, весь этот бардак идет от допущения частных случаев и запрета общих. Если бы имелась возможность сделать полное объявление функции-члена до ее определения — хоть во friend, хоть непосредственно — этих проблем не возникало бы, и принцип "must be declared before use" продолжал бы исправно работать. Вот тогда можно было бы говорить о "частичном определении класса", и в нем не было бы ничего плохого.

ЕМ>>Насколько я помню, этого не было в ранних реализациях C++.


S>А я вот не помню, чтобы этого в C++ не было.


Разве изначально была возможность использовать данные-члены до их определения?

S>написано что inline в определении класса.


Угу, а почему написано? Ну, кроме того, что "так решили"? Кому оно могло бы помешать в объявлении функции? Если вдруг до определения встретился вызов — генерировать код вызова с выдачей предупреждения, и всего делов. А если бы inline гарантировал непосредственную вставку, то выдавать ошибку.
Re[6]: Почему нельзя предварительно объявить дружественную функ
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 26.09.23 16:26
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>вы хотите объявить другом класса закрытый (приватный) метод другого класса?


Нет, открытый. Проблема лишь в том, что язык не дает мне заранее объявить этот метод — хоть во friend, хоть независимо, и я пытаюсь понять, почему.
Re[7]: Почему нельзя предварительно объявить дружественную ф
От: B0FEE664  
Дата: 26.09.23 16:53
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

BFE>>вы хотите объявить другом класса закрытый (приватный) метод другого класса?

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

Почему компиляторы такие — я не знаю, однако замечу, что вызов публичного невиртуального метода класса равносилен вызову дружественной функции с передачей в качестве первого параметра указателя на объект. Т.е. вызовы x.method() и method(&x) столь мало различаются, что заострять на этом внимание как-то странно. Поэтому пример приведённый so5team мне кажется адекватной заменой.
Никакой разницы в доступе между этим
class A;
class B;

class B
{
  public: void AccessToA(A& a);
};

class A 
{
  friend void B::AccessToA(A& a);
  private: int m_iA{0};  
};

void B::AccessToA(A& a)
{
    std::cout << a.m_iA << std::endl;
}

int main()
{
    A a;
    B b;
    b.AccessToA(a);
    return 0;
}

и этим:
class A;
class B;

class A 
{
  friend void AccessToA(B* b, A& a);
  private: int m_iA{0};  
};


class B
{
  friend void AccessToA(B* b, A& a);
};

void AccessToA(B*, A& a)
{
    std::cout << a.m_iA << std::endl;
}

int main()
{
    A a;
    B b;
    AccessToA(&b, a);
    return 0;
}

нет.

И ещё: дружественные функции вообще не нужны. Я знаю ровно одно реальное применение — это boost::intrusive_ptr, но и то, дружественные функции там потребовались исключительно для экономии времени и пространста памяти.
И каждый день — без права на ошибку...
Отредактировано 26.09.2023 16:56 B0FEE664 . Предыдущая версия .
Re[13]: Почему нельзя предварительно объявить дружественную функ
От: so5team https://stiffstream.com
Дата: 26.09.23 17:00
Оценка: :)
Здравствуйте, Евгений Музыченко, Вы писали:

S>>Для того, чтобы было вами подтверждено решение есть.


ЕМ>Так я согласен с тем, что оно есть. По большому счету, почти для любой проблемы найдется худо-бедно подходящее решение. Но любое решение, кроме непосредственного объявления одного метода дружественным, получается неестественным, и оттого уродливым.


Хватить бредить: у вас не просто требование объявить дружественным метод какого-то класса. У вас задача сделать это для класса, которого еще нет в области видимости. Типа "я когда-то где-то объявлю класс B у которого будет B::f вот именно с такой сигнатурой, мамойклянусь, адын-адын". Вы изначально хотите черезжопное решение и вопрошаете почему черезжопно не получается так, чтобы было нечерезжопно.

S>>Такая, что мы начинаем делать частичное описание класса.


ЕМ>Еще раз: компилятору нет нужды делать для такого случая описание класса. Ему необходимо любым способом запомнить сигнатуру функции,


Зачем это компилятору? Как будто у него забот мало.

А да, нужно же позволить Евгению Музыченко удалять гланды через задний проход...

S>>Вы много херни говорите.


ЕМ>Вы, когда Вас заедает на каком-то принципиальном вопросе, говорите не меньше.


С большей долей вероятности я говорю вещи, которые вы не можете уразуметь.

ЕМ>>>С чего бы вдруг

S>>>> friend void B::f(int a) const;
ЕМ>>>стало достаточным для
S>>>> b.f(0);
ЕМ>>>?

S>>Да потому, что это согласуется вот с таким, например (цинк)


ЕМ>С таким оно не согласуется


Согласуется. Как только функция объявляется как friend, она становится доступной для поиска имен при вызове.

ЕМ>как верно отметил watchmaker, поскольку у функций-членов могут быть дополнительные атрибуты, потребные для формирования кода вызова.


Если вы разуете глаза и засунете свой многолетний опыт в место куда не заглядывает Солнце для того, чтобы чему-то научиться, то увидите, что в примере приведены атрибуты достаточные для вызова.

ЕМ>Вообще, весь этот бардак идет от допущения частных случаев и запрета общих.


Роль говна в вашей башке не меньше, поверьте.

ЕМ>Если бы имелась возможность сделать полное объявление функции-члена до ее определения — хоть во friend, хоть непосредственно — этих проблем не возникало бы


В рамках класса вы можете это сделать ну буквально как два пальца.
Вы же не этого хотите. Вы хотите уметь объявлять методы класса до объявления самого класса.
Чтобы было так: класса еще нет, но метод евоный уже есть.

Был бы C++ другим языком, в котором не было бы классов, а были бы структуры, а методом бы считалась любая функция, получающая первым параметром ссылку на структуры, что-то вроде:
struct Demo { ... };

void update(Demo & self, ...) {...}
void print(Demo & self, ...) {...}
...
Demo d;
d.update(...);

то ваши пожелания еще можно было бы понять. Но тут как в анекдоте: если бы у бабушки, то...

ЕМ>и принцип "must be declared before use" продолжал бы исправно работать.


Послушайте, а вы точно на C++ пишете? Попробуйте в C++ дернуть функцию, которая не была объявлена/определена до вызова. Проверьте, работает ли принцип "must be declared before use".

ЕМ>>>Насколько я помню, этого не было в ранних реализациях C++.


S>>А я вот не помню, чтобы этого в C++ не было.


ЕМ>Разве изначально была возможность использовать данные-члены до их определения?


Насколько я помню, да. В рамках класса разрешение имен выполнялось чуть иначе: сперва компилятор должен был обработать все описание класса, лишь затем производить разрешение имен.

S>>написано что inline в определении класса.


ЕМ>Угу, а почему написано?


Вы вообще тупой?

Речь о том, что тело метода задается прямо в описании класса. Это и есть inline в определении. Т.е. возможность писать так:
class Demo {
  void method() {
    std::cout << "Hello, stupid!" << std::endl;
  }
  ...
};

а не так (и только так):
class Demo {
  void method();
  ...
};

void Demo::method() {
  std::cout << "Hello, stupid!" << std::endl;
}

То, что компилятор автоматически навешивает на подобные определения внутри описания класса атрибут inline -- это просто бонус (нужный для того, чтобы линкер мог выбросить лишнее, если я понимаю правильно).
Re[12]: private nested class
От: Sm0ke Россия ksi
Дата: 26.09.23 19:10
Оценка:
Здравствуйте, so5team, Вы писали:

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


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


S>>>Не уверен, что вы поняли меня, но уверен, что не понял вас.


S>>Отдельный внутренний класс internal_env_iface_t можно поместить в private: часть другого класса.

S>>Таким образом можно разграничить использование этого internal_env_iface_t
S>>В си++ есть такая возможность. В этом может помочь nested classes
S>>https://en.cppreference.com/w/cpp/language/nested_types

S>Вот это и заставляет меня думать, что вы не поняли меня.

S>Если делать internal_env_iface_t закрытым, то чтобы дать к нему доступ из разных мест придется эти разные места прописывать в качестве друзей. А именно этого и хочется избежать.

Ну как понимаю класть всех использующих internal_env_iface_t в один большой nest не предлагать?

Но можно же наследовать nest-ы!
Положить использующих internal_env_iface_t по своим разным nest-ам,
Те nest-ы отнаследовать от nest-а, в котором лежит internal_env_iface_t как protected:

Вот и уменьшилось число friend указаний.
Да, я теоретизирую. Да, на практике могут быть нюансы.

И кстати стоило бы проверить: Если в качестве friend указан nest, то nested классы получат ли доступ тоже? А их наследники?
upd: Answer "nested классы" да; 'их наследники' нет;

--

Такие штуки вообще могут быть решены через модули, партишены и выборочный экспорт имён.
Отредактировано 26.09.2023 19:58 Sm0ke . Предыдущая версия .
Re[13]: private nested class
От: so5team https://stiffstream.com
Дата: 27.09.23 05:06
Оценка:
Здравствуйте, Sm0ke, Вы писали:

S>Ну как понимаю класть всех использующих internal_env_iface_t в один большой nest не предлагать?


Нет.

S>И кстати стоило бы проверить: Если в качестве friend указан nest, то nested классы получат ли доступ тоже? А их наследники?

S>upd: Answer "nested классы" да; 'их наследники' нет;

Friendship Is Not Inherited Or Transitive (c)
Re[6]: Почему нельзя предварительно объявить дружественную функ
От: so5team https://stiffstream.com
Дата: 27.09.23 05:11
Оценка: +1
Здравствуйте, B0FEE664, Вы писали:

BFE>Правильно ли я понимаю, что вы хотите объявить другом класса закрытый (приватный) метод другого класса?


Нет. Он хочет для класса A объявить другом метод класса B при том, что определения класса B нет в точке объявления класса A. Т.е. что-то вроде:
class B; // Будет определен хз где, возможно, в закрытой части библиотеки.

class A { // А вот определение для A делается прямо здесь.
  // Вот какую штуку хочет ТС:
  friend void B::some_method(A &); // Музыченко готов присягнуть компилятору кровью
                                   // что в классе B будет some_method.
  ...
};

Как я понял, ключевое то, что определение B он не может (не хочет) делать до определения класса A.
Re[7]: Почему нельзя предварительно объявить дружественную функ
От: YuriV  
Дата: 27.09.23 19:30
Оценка:
Здравствуйте, so5team, Вы писали:

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


BFE>>Правильно ли я понимаю, что вы хотите объявить другом класса закрытый (приватный) метод другого класса?


S>Нет. Он хочет для класса A объявить другом метод класса B при том, что определения класса B нет в точке объявления класса A. Т.е. что-то вроде:

S>
S>class B; // Будет определен хз где, возможно, в закрытой части библиотеки.

S>class A { // А вот определение для A делается прямо здесь.
S>  // Вот какую штуку хочет ТС:
S>  friend void B::some_method(A &); // Музыченко готов присягнуть компилятору кровью
S>                                   // что в классе B будет some_method.
S>  ...
S>};
S>

S>Как я понял, ключевое то, что определение B он не может (не хочет) делать до определения класса A.

Здесь вопрос соглашения — в С++ мы не можем апеллировать к структуре незавершённого типа — нигде, у нас есть только имя типа, иначе была бы возможна настоящая вэлью рекурсия (не через теневую ссылку или указатель) что есть нонсенс.
class A { A _a; };

В тех пределах, что хочет ТС требование выполнимо, но возможен гемор с сигнатурами. А то, что метода может не существовать компайлеру похрен. Нас же не смущает, что самого дружественного класса может не существовать.

// unit1.h
class B; // Будет определен хз где, возможно, в закрытой части библиотеки.

class A {
  int _value{3};
  friend void B::some_method(A &); // вот это запрещено природой незавершённого типа С++ и всё
};

// unit2.h
class A; // Будет определен хз где, возможно, в закрытой части библиотеки.

class B {
  void some_method(A &);
};

// unit2.cpp
#include "unit1.h"

void B::some_method(A & a) {
  a._value; // compiler checks access right for A privates
}
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.