Здравствуйте, 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]: Почему нельзя предварительно объявить дружественную функ
Здравствуйте, Евгений Музыченко, Вы писали:
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, но и то, дружественные функции там потребовались исключительно для экономии времени и пространста памяти.
Здравствуйте, Евгений Музыченко, Вы писали:
S>>Для того, чтобы было вами подтверждено решение есть.
ЕМ>Так я согласен с тем, что оно есть. По большому счету, почти для любой проблемы найдется худо-бедно подходящее решение. Но любое решение, кроме непосредственного объявления одного метода дружественным, получается неестественным, и оттого уродливым.
Хватить бредить: у вас не просто требование объявить дружественным метод какого-то класса. У вас задача сделать это для класса, которого еще нет в области видимости. Типа "я когда-то где-то объявлю класс B у которого будет B::f вот именно с такой сигнатурой, мамойклянусь, адын-адын". Вы изначально хотите черезжопное решение и вопрошаете почему черезжопно не получается так, чтобы было нечерезжопно.
S>>Такая, что мы начинаем делать частичное описание класса.
ЕМ>Еще раз: компилятору нет нужды делать для такого случая описание класса. Ему необходимо любым способом запомнить сигнатуру функции,
Зачем это компилятору? Как будто у него забот мало.
А да, нужно же позволить Евгению Музыченко удалять гланды через задний проход...
S>>Вы много херни говорите.
ЕМ>Вы, когда Вас заедает на каком-то принципиальном вопросе, говорите не меньше.
С большей долей вероятности я говорю вещи, которые вы не можете уразуметь.
ЕМ>>>С чего бы вдруг S>>>> friend void B::f(int a) const; ЕМ>>>стало достаточным для S>>>> b.f(0); ЕМ>>>?
S>>Да потому, что это согласуется вот с таким, например (цинк)
ЕМ>С таким оно не согласуется
Согласуется. Как только функция объявляется как friend, она становится доступной для поиска имен при вызове.
ЕМ>как верно отметил watchmaker, поскольку у функций-членов могут быть дополнительные атрибуты, потребные для формирования кода вызова.
Если вы разуете глаза и засунете свой многолетний опыт в место куда не заглядывает Солнце для того, чтобы чему-то научиться, то увидите, что в примере приведены атрибуты достаточные для вызова.
ЕМ>Вообще, весь этот бардак идет от допущения частных случаев и запрета общих.
Роль говна в вашей башке не меньше, поверьте.
ЕМ>Если бы имелась возможность сделать полное объявление функции-члена до ее определения — хоть во friend, хоть непосредственно — этих проблем не возникало бы
В рамках класса вы можете это сделать ну буквально как два пальца.
Вы же не этого хотите. Вы хотите уметь объявлять методы класса до объявления самого класса.
Чтобы было так: класса еще нет, но метод евоный уже есть.
Был бы C++ другим языком, в котором не было бы классов, а были бы структуры, а методом бы считалась любая функция, получающая первым параметром ссылку на структуры, что-то вроде:
то ваши пожелания еще можно было бы понять. Но тут как в анекдоте: если бы у бабушки, то...
ЕМ>и принцип "must be declared before use" продолжал бы исправно работать.
Послушайте, а вы точно на C++ пишете? Попробуйте в C++ дернуть функцию, которая не была объявлена/определена до вызова. Проверьте, работает ли принцип "must be declared before use".
ЕМ>>>Насколько я помню, этого не было в ранних реализациях C++.
S>>А я вот не помню, чтобы этого в C++ не было.
ЕМ>Разве изначально была возможность использовать данные-члены до их определения?
Насколько я помню, да. В рамках класса разрешение имен выполнялось чуть иначе: сперва компилятор должен был обработать все описание класса, лишь затем производить разрешение имен.
S>>написано что inline в определении класса.
ЕМ>Угу, а почему написано?
Вы вообще тупой?
Речь о том, что тело метода задается прямо в описании класса. Это и есть inline в определении. Т.е. возможность писать так:
То, что компилятор автоматически навешивает на подобные определения внутри описания класса атрибут inline -- это просто бонус (нужный для того, чтобы линкер мог выбросить лишнее, если я понимаю правильно).
Здравствуйте, 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 классы" да; 'их наследники' нет;
--
Такие штуки вообще могут быть решены через модули, партишены и выборочный экспорт имён.
Здравствуйте, Sm0ke, Вы писали:
S>Ну как понимаю класть всех использующих internal_env_iface_t в один большой nest не предлагать?
Нет.
S>И кстати стоило бы проверить: Если в качестве friend указан nest, то nested классы получат ли доступ тоже? А их наследники? S>upd: Answer "nested классы" да; 'их наследники' нет;
Friendship Is Not Inherited Or Transitive (c)
Re[6]: Почему нельзя предварительно объявить дружественную функ
Здравствуйте, B0FEE664, Вы писали:
BFE>Правильно ли я понимаю, что вы хотите объявить другом класса закрытый (приватный) метод другого класса?
Нет. Он хочет для класса A объявить другом метод класса B при том, что определения класса B нет в точке объявления класса A. Т.е. что-то вроде:
class B; // Будет определен хз где, возможно, в закрытой части библиотеки.class A { // А вот определение для A делается прямо здесь.
// Вот какую штуку хочет ТС:friend void B::some_method(A &); // Музыченко готов присягнуть компилятору кровью
// что в классе B будет some_method.
...
};
Как я понял, ключевое то, что определение B он не может (не хочет) делать до определения класса A.
Re[7]: Почему нельзя предварительно объявить дружественную функ
Здравствуйте, 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.hclass B; // Будет определен хз где, возможно, в закрытой части библиотеки.class A {
int _value{3};
friend void B::some_method(A &); // вот это запрещено природой незавершённого типа С++ и всё
};
// unit2.hclass 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
}