Re[3]: variadic
От: rg45 СССР  
Дата: 25.09.23 13:58
Оценка: :)))
Здравствуйте, Sm0ke, Вы писали:

S>ради эксперимента

S>Попробовал этот трюк провернуть с вариадиками, но что-то пошло не так: https://godbolt.org/z/9rdGfzn76

Перед кем ты бисер мечешь? Его от шаблонов корежит, как вампира от чеснока, он макросы использует, только чтоб к шаблонам не прикасаться. А ты ему вариадики.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[5]: Почему нельзя предварительно объявить дружественную функ
От: so5team https://stiffstream.com
Дата: 25.09.23 19:17
Оценка: +3
Здравствуйте, Sm0ke, Вы писали:

S>А если их много — всё это ручками писать?


Если у класса A слишком много друзей, то с классом A явно что-то не так.
Re[6]: Почему нельзя предварительно объявить дружественную ф
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 25.09.23 13:06
Оценка: :))
Здравствуйте, rg45, Вы писали:

R>Тебе самому не надоело жить в выдуманном мире?


Это еще большой вопрос, у кого мир более выдуманный. Я-то языком пользуюсь сугубо прагматично, для получения кода, надежно работающего в пределах заданных допущений, и не претендующего ни на полное соответствие Стандарту, ни на "каноничность". Так уж сделали язык, что для этого его регулярно приходится гнуть об колено. А Вы вместе с теми, кто меня этим попрекает, ратуете за рафинированную корректность и чуть ли не математическую чистоту. Кто из нас ближе к реальности?

R>Подучил бы уже язык, с которым работаешь, хотя бы на уровне "объявления-определения" да "аз-буки".


На уровне "для работы" я его понимаю гораздо лучше среднего заурядного плюсовика, который тупо кодит алгоритмы по заданию. Изучать же на уровне, достаточном для сдачи экзамена или преподавания, не вижу ни малейшего смысла, ибо результатов моей работы это ни разу не улучшит.
Re[7]: Почему нельзя предварительно объявить дружественную ф
От: so5team https://stiffstream.com
Дата: 25.09.23 13:29
Оценка: +2
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Я-то языком пользуюсь сугубо прагматично, для получения кода, надежно работающего в пределах заданных допущений, и не претендующего ни на полное соответствие Стандарту, ни на "каноничность".


Если ваш код не соответствует стандарту, то он запросто может перестать работать при обновлении компилятора.

Да, да, тут уже все знают, что это не ваш случай и что у вас компиляторов этих всего 1-2 и обчелся, да к тому же все они от Microsoft.
Но доблести, увы, в этом нет. И у людей, которые набили шишек при переносе кода между vc++, gcc и clang (хотя бы этими тремя), подобная бравада одобрения не вызывает, мягко говоря.
Re[8]: Почему нельзя предварительно объявить дружественную ф
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 25.09.23 14:20
Оценка: :))
Здравствуйте, so5team, Вы писали:

S>Если ваш код не соответствует стандарту, то он запросто может перестать работать при обновлении компилятора.


Теоретически — может. А практически — ни разу не переставал. Он даже компилироваться ни разу не переставал — только новые предупреждения появлялись при /Wall.

Вот только практика показывает, что код, полностью соответствующий определенному стандарту, запросто может перестать компилироваться и/или работать при обновлении компилятора, ориентированного на более новые стандарты, и не поддерживающего старых (по крайней мере, по умолчанию). С этим что будем делать?

Вообще, требование полного соответствия любого кода стандарту — это, грубо говоря, как требование надежной работы изделия в пределах хотя бы всей планеты — и на экваторе, и на полюсах, и в стратосфере, и под водой. Для некоторых изделий это необходимо, но для подавляющего большинства вполне достаточно "разумных условий". Ежели у кого автомобиль, сделанный в Испании, плохо заводится в Якутии или на Аляске, он почему-то не спешит объявлять его "не соответствующими стандарту автомобиля".

S>Но доблести, увы, в этом нет. И у людей, которые набили шишек при переносе кода между vc++, gcc и clang (хотя бы этими тремя), подобная бравада одобрения не вызывает, мягко говоря.


Это не доблесть и не бравада. Это вполне разумный инженерный подход, который еще не так давно преобладал и в C, и в C++, а сейчас продолжает преобладать в большинстве других языков, для которых нет единых стандартов. А вот в C++ из стандарта сделали фетиш.
Re[7]: Почему нельзя предварительно объявить дружественную функ
От: B0FEE664  
Дата: 26.09.23 08:50
Оценка: 14 (1)
Здравствуйте, Евгений Музыченко, Вы писали:

S>>- нужно минимизировать объем кода, который имеет доступ к потрохам класса A (т.е. до одной функции/метода);

S>>- сделать это нужно в условиях, когда перед определением класса A нельзя сделать определение класса B (которому и нужен доступ к потрохам B).
ЕМ>Верно.

class A;
class B;
class OnlyA{ friend class A; private: OnlyA(){} };

class A {
  friend void modify_A(A &, B &, OnlyA);
public:
  void f(B & consumer);
private: int m_iA{0};  
};

class B {
  friend void modify_A(A &, B &, OnlyA);

public:
private: int m_iB{0};  
};

void A::f(B & b) {
  modify_A(*this, b, OnlyA{});
}

void modify_A(A & a, B & b, OnlyA) {
  // Тут есть доступ и к потрохам A, и к потрохам B.
  a.m_iA++;
  b.m_iB++;
}

int main()
{
    A a;
    B b;
    a.f(b);
  //modify_A(a, b, OnlyA{});

  return 0;
}


Можно, но зачем?
И каждый день — без права на ошибку...
Re: Почему нельзя предварительно объявить дружественную функ
От: Sm0ke Россия ksi
Дата: 25.09.23 12:03
Оценка: 12 (1)
Здравствуйте, Евгений Музыченко

Вы хотите указать метод того класса, который ещё не определён, как дружественный?
Если этот класс, который с дружесвенным методом, принимать как параметр шаблона, то это может получится

#include <iostream>

template <typename T>
struct restricted
{
  friend void T::go(const restricted &) const;
private:
  int value{55};
};

struct agent
{
  template <typename T>
  void go(const T & r) const
  {
    std::cout << r.value << '\n';
  }
};

int main()
{
  restricted<agent> v;
  agent a;
  a.go(v);
  return 0;
}

Output:

55


Если имя метода, который вы хотите указать как friend, сделать зависимым от шаблонного параметра, то
разрешение имён откладывается до мемента инстанцирования.

Про это есть видео на ютуб канале Константина Владимирова
  Магистерский курс C++ (МФТИ, 2022-2023). Лекция 4. Разрешение имён в шаблонах и One Definition Rule.
https://www.youtube.com/watch?v=8mCSDR1NpoU
Отредактировано 25.09.2023 12:32 Sm0ke . Предыдущая версия . Еще …
Отредактировано 25.09.2023 12:12 Sm0ke . Предыдущая версия .
Отредактировано 25.09.2023 12:11 Sm0ke . Предыдущая версия .
Re: Почему нельзя предварительно объявить дружественную функцию-член?
От: watchmaker  
Дата: 24.09.23 18:23
Оценка: 10 (1)
Здравствуйте, Евгений Музыченко, Вы писали:

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


Кажется, это совсем не так.

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

Но для функции-члена у тебя может быть разный код вызова для виртуального метода и для невиртуального. То есть для классов
struct S {
    void foo(int);
};

struct B {
      virtual void foo(int);
};


код вызова S::foo или B::foo во многих ABI будет совсем разный и только одной декларации void B::foo(int); для компилятора недостаточно.

Конечно, ты можешь потребовать дополнительных ограничений на то, что нужно при forward-деклараци обязательно указывать ещё и виртуальность функции.
Но и этого недостаточно, так как код виртуального вызова ещё и зависит от порядка методов в классе.
Например, для классов

struct U {
    virtual void bar(int);
    virtual void foo(int);
};

struct V {
    virtual void foo(int);
    virtual void bar(int);
};


вызов метода foo делается разным кодом, потому что в ABI (например, в itanium-cxx-abi) есть зависимость от порядка описания членов при построении виртуальной таблицы.
Но если у компилятора есть только декларация virtual void U::bar(int);, то у него нет шансов узнать по какому смещению будет расположен в virtual-table указатель на метод:
U* u = ...;
u->foo(1);


А ещё есть и виртуально наследование, которое тоже может вносить свои особенности (так как нужно корректировать при вызове указатель this).
Вот и получается, что для вызова функции в общем случае компилятору нужно видеть очень много других деталей про класс. Как минимум все остальные функции в нём и во всех его базах, и всю схему наследований.
Re[4]: Почему нельзя предварительно объявить дружественную ф
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 24.09.23 20:27
Оценка: :)
Здравствуйте, rg45, Вы писали:

R>в общем случае, свободную функцию тоже сначала нужно объявить, прежде чем ее можно будет объявить дружественной


А чем это обосновано?

R>Нужно объяснять, почему подобное исключение невозможно сделать для дружественных функций-членов


Понятно, что при условии вышеприведенного правила это невозможно. А для чего необходимо само правило?
Re[7]: Почему нельзя предварительно объявить дружественную ф
От: rg45 СССР  
Дата: 25.09.23 13:54
Оценка: :)
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Это еще большой вопрос, у кого мир более выдуманный. Я-то языком пользуюсь сугубо прагматично, для получения кода, надежно работающего в пределах заданных допущений, и не претендующего ни на полное соответствие Стандарту, ни на "каноничность". Так уж сделали язык, что для этого его регулярно приходится гнуть об колено. А Вы вместе с теми, кто меня этим попрекает, ратуете за рафинированную корректность и чуть ли не математическую чистоту. Кто из нас ближе к реальности?


Да какое там, нафиг, полное соответствие, когда ты азов не знаешь. Невежество на невежестве, так еще и гордится этим. Языком он пользуется. Как шимпанзе смартфоном.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 25.09.2023 14:11 rg45 . Предыдущая версия .
Re[4]: Почему нельзя предварительно объявить дружественную функ
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 25.09.23 17:07
Оценка: +1
Здравствуйте, so5team, Вы писали:

S>Какие-то странные поиски того, чего нет, когда можно решить "в лоб":


Это ж тоже кривой костыль. Чем это лучше, как сделать вспомогательную функцию открытым членом класса A, и вызывать ее из класса B? "В лоб" — это объявить дружественным весь класс B, и забить на принцип минимальных прав.
Re[6]: Почему нельзя предварительно объявить дружественную функ
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 25.09.23 20:05
Оценка: :)
Здравствуйте, so5team, Вы писали:

ЕМ>>Чем это лучше, как сделать вспомогательную функцию открытым членом класса A, и вызывать ее из класса B?


S>Тем, что тогда ее сможет вызвать кто угодно, а не только класс B. Ваш К.О.


Скажите, К.О., а кто из перечисленных не сможет вызвать функцию modify_A?

S>- нужно минимизировать объем кода, который имеет доступ к потрохам класса A (т.е. до одной функции/метода);

S>- сделать это нужно в условиях, когда перед определением класса A нельзя сделать определение класса B (которому и нужен доступ к потрохам B).

Верно.

S>Если вам насрать на эту задачу и вы можете просто сделать класс B другом для класса A, то зачем весь этот кипешь?


Вопрос, напоминаю, был не в том, как это сделать (способы решения есть разные, и все они в чем-то кривые), а в том, почему язык не допускает указания друзей-членов наперед, хотя принципиальных препятствий этому нет.
Re[7]: Почему нельзя предварительно объявить дружественную ф
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 25.09.23 20:15
Оценка: :)
Здравствуйте, Евгений Музыченко, Вы писали:


ЕМ>Это еще большой вопрос, у кого мир более выдуманный. Я-то языком пользуюсь сугубо прагматично, для получения кода, надежно работающего в пределах заданных допущений, и не претендующего ни на полное соответствие Стандарту, ни на "каноничность".


Как ты изящно говнокод описал


ЕМ>На уровне "для работы" я его понимаю гораздо лучше среднего заурядного плюсовика, который тупо кодит алгоритмы по заданию.


Тупо кодит алгоритмы по заданию — это джун на испытательном сроке. Да и то, язык таки надо знать. И уж никак не средний заурядный плюсовик
Маньяк Робокряк колесит по городу
Re[9]: Почему нельзя предварительно объявить дружественную ф
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 25.09.23 20:17
Оценка: :)
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Это не доблесть и не бравада. Это вполне разумный инженерный подход, который еще не так давно преобладал и в C, и в C++, а сейчас продолжает преобладать в большинстве других языков, для которых нет единых стандартов. А вот в C++ из стандарта сделали фетиш.


Хм. "Не делайте фетиш из стандарта". Надо записать
Маньяк Робокряк колесит по городу
Re[5]: variadic
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 25.09.23 20:20
Оценка: :)
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Я не раз подчеркивал, что регулярно применяю шаблоны по их прямому назначению. А то, что вместо вашей любимой SFINAE-магии мне часто удобнее применить макрос, который и проще, и понятнее, а часто еще и компактнее по коду — звиняйте, уж как есть.


SFINAE-магия давно устарела
Маньяк Робокряк колесит по городу
Re[7]: Почему нельзя предварительно объявить дружественную функ
От: so5team https://stiffstream.com
Дата: 26.09.23 05:13
Оценка: +1
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>>>Чем это лучше, как сделать вспомогательную функцию открытым членом класса A, и вызывать ее из класса B?


S>>Тем, что тогда ее сможет вызвать кто угодно, а не только класс B. Ваш К.О.


ЕМ>Скажите, К.О., а кто из перечисленных не сможет вызвать функцию modify_A?


Например тот, кто не имеет валидной ссылки на экземпляр B.

S>>- нужно минимизировать объем кода, который имеет доступ к потрохам класса A (т.е. до одной функции/метода);

S>>- сделать это нужно в условиях, когда перед определением класса A нельзя сделать определение класса B (которому и нужен доступ к потрохам B).

ЕМ>Верно.


Ну тогда вот это -- ""В лоб" — это объявить дружественным весь класс B, и забить на принцип минимальных прав." -- не есть решение поставленной задачи.

S>>Если вам насрать на эту задачу и вы можете просто сделать класс B другом для класса A, то зачем весь этот кипешь?


ЕМ>Вопрос, напоминаю, был не в том, как это сделать (способы решения есть разные, и все они в чем-то кривые), а в том, почему язык не допускает указания друзей-членов наперед, хотя принципиальных препятствий этому нет.


Точнее было бы сказать, что это вы не видите принципиальных препятствий. Вам ув.тов.watchmaker уже описал некоторые причины, вас не удовлетворило, вы начали вести себя в стиле "ты говоришь мне просто правду, а я ужасную хочу!"

Подумайте еще вот о чем: сейчас язык допускает либо упреждающее объявление класса (вроде class A;), либо полное определение (class A {...}). Ваше предложение о том, чтобы можно было не давая полного определения класса сделать предварительное объявление его метода в качестве друга означает, что нужно ввести еще одно состояние. Нечто вроде частично определенный класс. И, как по мне, то нафиг нам в C++ такое щасте, язык итак уже усложнен донельзя.
Re[14]: Почему нельзя предварительно объявить дружественную ф
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 26.09.23 07:35
Оценка: :)
Здравствуйте, so5team, Вы писали:

ЕМ>>Какова доля "истинно кросс-платформенного" кода на C++ во всем объеме кода на C++?


S>ХЗ. В моей около тридцатилетней практике что-то около 100%.


Это наводит на мысль о том, что Вы работаете преимущественно в сфере кросс-платформенного опенсорса.

S>подозреваю, что когда человек пишет драйвера для Windows на x86/x64, то весь мир C++ для него выглядит как прибитый гвоздями для одной платформы.


Человек говорит о том, что кросс-платформенность не является (и не должна быть) сверхцелью. Это лишь одно из свойств ПО, но далеко не всегда ключевое, и далеко не всегда даже желательное.

ЕМ>>А если вычесть формально существующий, но не используемый сколько-нибудь активно?


S>Не распарсил.


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

ЕМ>>Чем дальше задача уходит от конкретной платформы, тем меньше остается оснований делать ее на C++.


S>Продавцы Qt смотрят на вас как на...


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

S>Вы пытаетесь доказать, что подавляющая часть C++ного кода изначально прибита гвоздями к одной платформе и одной версии одного компилятора?


Я пытаюсь объяснить, что и кросс-платформенность, и кросс-компиляторность — это просто возможности языка. Которые могут быть использованы в конкретном ПО, а могут и не быть.

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

S>если вы не соблюдаете стандарт в коде, то даже в рамках одной платформы вы можете получить проблемы. Просто сменив компилятор.


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

S>Chrome построен на базе открытого ПО. Можно ли представить себе современный Интернет без Chrome и разработок на его основе?


Лучше было бы, если б было можно. То, что этого нельзя представить, ни разу не положительная сторона.

S>Обычные компьютеры обычных пользователей перестали тормозить уже лет 20 как


Пользователи обычных компьютеров смотрят на Вас с недоумением. Или Вы о тормозах при вводе текста в простейшие редакторы типа "блокнот"?

S>Но Qt все еще продается.


И еще долго будет продаваться. Но не будет победно шествовать по планете.

S>писать в user-space на C++ есть еще достаточно резонов.


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

Поэтому ко всему нужно относиться разумно, а не чисто формально.
Re[8]: read_only ячейка
От: Sm0ke Россия ksi
Дата: 26.09.23 11:37
Оценка: +1
Здравствуйте, Евгений Музыченко, Вы писали:

S>>
S>>public read_only protected full_access:
S>>


ЕМ> ... А какой смысл в full_access при наличии public?


Чтобы можно было уточнить для мемберов ниже — какой же к ним доступ из derived классов, а какой из внешки.
public read_only protected read_only:


--

private: это просто private

(вроде логично, что есть const — и private read_only: выглядит лишним)
Отредактировано 26.09.2023 11:39 Sm0ke . Предыдущая версия .
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[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.
Почему нельзя предварительно объявить дружественную функцию-член?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 24.09.23 17:30
Оценка:
Язык допускает предварительное объявление (forward declaration) дружественной (friend) обычной (non-member) функции, но почему-то не допускает такого для функции-члена, класс которой не определен полностью (только предварительно объявлен — "class C;").

Откуда растет это ограничение? Вроде как предварительная декларация функции-члена другого класса обеспечивает компилятор всей информацией об этой функции (по сути, ему должно быть достаточно сигнатуры). Чего здесь не хватает компилятору?
Re: Почему нельзя предварительно объявить дружественную функ
От: rg45 СССР  
Дата: 24.09.23 18:38
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Язык допускает предварительное объявление (forward declaration) дружественной (friend) обычной (non-member) функции...


И сходу неправильно. Объявление функции дружественной не является предварительным объявлением функции:

http://coliru.stacked-crooked.com/a/92590ae0842c85d9

struct A
{
    friend void foo();
};

int main()
{
    foo(); // error: 'foo' was not declared in this scope
}


Язык допускает объявление дружественной функции без ее предварительного объявления только для функций из того же пространства имен, что и сам класс. А для того, чтобы объявить дружественной функцию из другого пространства имен, ее нужно будет сначала объявить. И сделать это нужно будет в пространстве имен функции.

http://coliru.stacked-crooked.com/a/5da8e14983baa2f4

namespace ns
{

void foo();
    
} // namespace ns

struct A
{
    friend void ns::foo(); // Ok
    friend void ns::bar(); // error: 'void ns::bar()' should have been declared inside 'ns'
};


Я думаю, что если ты потрудишься переформулировать свой вопрос без использования ошибочных утверждений, то ответ найдется автоматически.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 24.09.2023 18:48 rg45 . Предыдущая версия .
Re[2]: Почему нельзя предварительно объявить дружественную функцию-член?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 24.09.23 19:23
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>Декларация обычной функции позволяет компилятору генерировать код её вызова.


Спасибо за развернутые рассуждения, но при чем они здесь? Если я в классе A указываю дружественную функцию из класса B, это сообщает компилятору лишь то, что эта функция будет иметь право доступа к закрытым членам класса A, и ничего больше. Если бы компилятор выдавал ошибку в точке вызова этой функции, пока ее класс полностью не определен, я бы не удивился. Но он выдает ошибку именно в точке объявления friend, и это странно.
Re[2]: Почему нельзя предварительно объявить дружественную функ
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 24.09.23 19:29
Оценка:
Здравствуйте, rg45, Вы писали:

R>И сходу неправильно.


Точнее, Вы с ходу стали искать, к чему прицепиться, и таки нашли.

R>Объявление функции дружественной не является предварительным объявлением функции


Само собой. Где я утверждал обратное?

R>Язык допускает объявление дружественной функции без ее предварительного объявления только для функций из того же пространства имен, что и сам класс.


Не понял, при чем здесь пространства имен. Если класс, членом которого является дружественная функция, полностью определен раньше класса, который указывает ее в качестве дружественной, то все компилируется, хотя все имена остаются в тех же пространствах.

R>для того, чтобы объявить дружественной функцию из другого пространства имен, ее нужно будет сначала объявить. И сделать это нужно будет в пространстве имен функции.


Мне нужно указать в качестве дружественной функцию, которая является членом класса, а не просто пространства имен.

R>если ты потрудишься переформулировать свой вопрос без использования ошибочных утверждений


Вы твердо уверены в том, что ошибочные утверждения делаю именно я?
Re[3]: Почему нельзя предварительно объявить дружественную ф
От: rg45 СССР  
Дата: 24.09.23 19:57
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Мне нужно указать в качестве дружественной функцию, которая является членом класса, а не просто пространства имен.


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

http://coliru.stacked-crooked.com/a/5da8e14983baa2f4

namespace ns
{

void foo();
    
} // namespace ns

struct A
{
    friend void ns::foo(); // Ok
    friend void ns::bar(); // error: 'void ns::bar()' should have been declared inside 'ns'
};


Исключением являются только дружественные функции из того же пространства имен, что и сам класс. Нужно объяснять, почему подобное исключение невозможно сделать для дружественных функций-членов, или сам напряжешь извилину?
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 24.09.2023 20:25 rg45 . Предыдущая версия . Еще …
Отредактировано 24.09.2023 20:14 rg45 . Предыдущая версия .
Отредактировано 24.09.2023 20:13 rg45 . Предыдущая версия .
Отредактировано 24.09.2023 20:04 rg45 . Предыдущая версия .
Отредактировано 24.09.2023 20:02 rg45 . Предыдущая версия .
Отредактировано 24.09.2023 20:02 rg45 . Предыдущая версия .
Отредактировано 24.09.2023 19:58 rg45 . Предыдущая версия .
Re[3]: Почему нельзя предварительно объявить дружественную ф
От: rg45 СССР  
Дата: 24.09.23 19:59
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

R>>Объявление функции дружественной не является предварительным объявлением функции


ЕМ>Само собой. Где я утверждал обратное?


Вот здесь
Автор: Евгений Музыченко
Дата: 24.09.23
ты утверждал, что

Язык допускает предварительное объявление (forward declaration) дружественной (friend) обычной (non-member) функции


Зачем отпираться, когда все ходы записаны?
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 24.09.2023 20:00 rg45 . Предыдущая версия .
Re[5]: Почему нельзя предварительно объявить дружественную ф
От: rg45 СССР  
Дата: 24.09.23 20:29
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>А чем это обосновано?

ЕМ>Понятно, что при условии вышеприведенного правила это невозможно. А для чего необходимо само правило?

Вот видишь, ты уже пытаешься переформулировать вопрос, это уже хорошо. Ты не торопись, подумай, глядишь, вопрос-то и отвалится, как дурацкий.

P.S. Что именно тебя удивляет — что функции из одних пространств имен нельзя объявлять в других?
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 24.09.2023 20:32 rg45 . Предыдущая версия .
Re[4]: Почему нельзя предварительно объявить дружественную ф
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 24.09.23 20:36
Оценка:
Здравствуйте, rg45, Вы писали:

R>Вот здесь
Автор: Евгений Музыченко
Дата: 24.09.23
ты утверждал, что


R>

R>Язык допускает предварительное объявление (forward declaration) дружественной (friend) обычной (non-member) функции


Я утверждал именно то, что в обычной практике (без разделения на пространства имен) он это допускает.

Осталось понять, почему не допускает это для функции-члена. Если объявить заранее пространство имен нельзя (тоже, кстати, искусственное ограничение), то класс-то заранее объявить можно.
Re[5]: Почему нельзя предварительно объявить дружественную ф
От: rg45 СССР  
Дата: 24.09.23 20:38
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Я утверждал именно то, что в обычной практике (без разделения на пространства имен) он это допускает.

ЕМ>Осталось понять, почему не допускает это для функции-члена. Если объявить заранее пространство имен нельзя (тоже, кстати, искусственное ограничение), то класс-то заранее объявить можно.

Да у тебя на каждом шагу "исскусственные ограничения". Тебе самому не надоело жить в выдуманном мире? Подучил бы уже язык, с которым работаешь, хотя бы на уровне "объявления-определения" да "аз-буки".
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 24.09.2023 20:40 rg45 . Предыдущая версия . Еще …
Отредактировано 24.09.2023 20:39 rg45 . Предыдущая версия .
Re[5]: Почему нельзя предварительно объявить дружественную ф
От: rg45 СССР  
Дата: 24.09.23 20:51
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Я утверждал именно то, что в обычной практике (без разделения на пространства имен) он это допускает.

ЕМ>Осталось понять, почему не допускает это для функции-члена. Если объявить заранее пространство имен нельзя (тоже, кстати, искусственное ограничение), то класс-то заранее объявить можно.

И кстати, стандарт языка не вводит термина "forward declaration" как такового. Есть "declaration" и есть "definition", а слово forward встречается исключительно в качестве второстепенного члена предложения в примерах и пояснениях к ним.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[5]: Почему нельзя предварительно объявить дружественную ф
От: rg45 СССР  
Дата: 25.09.23 00:01
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>...в обычной практике (без разделения на пространства имен)...


--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 25.09.2023 5:07 rg45 . Предыдущая версия . Еще …
Отредактировано 25.09.2023 0:08 rg45 . Предыдущая версия .
Re[2]: variadic
От: Sm0ke Россия ksi
Дата: 25.09.23 12:27
Оценка:
Здравствуйте, Sm0ke, Вы писали:

ради эксперимента
Попробовал этот трюк провернуть с вариадиками, но что-то пошло не так: https://godbolt.org/z/9rdGfzn76

#include <type_traits>
#include <iostream>

  template <typename Type, typename ... U>
  concept c_any_of = (std::is_same_v<Type, U> || ...);

template <typename ... T>
struct restricted
{
  template <c_any_of<T ...> U>
  friend void U::go(const restricted &) const;
private:
  int value{55};
};

struct agent_A
{
  template <typename T>
  void go(const T & r) const
  {
    std::cout << "A: " << r.value << '\n'; // fail
  }
};

struct agent_B
{
  template <typename T>
  void go(const T & r) const
  {
    std::cout << "B: " << r.value << '\n';
  }
};

int main()
{
  restricted<agent_A, agent_B> v;
  agent_A a;
  agent_B b;
  a.go(v);
  b.go(v);
  return 0;
}
Отредактировано 25.09.2023 12:28 Sm0ke . Предыдущая версия .
Re[6]: Почему нельзя предварительно объявить дружественную ф
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 25.09.23 13:14
Оценка:
Здравствуйте, rg45, Вы писали:

R>стандарт языка не вводит термина "forward declaration" как такового. Есть "declaration" и есть "definition", а слово forward встречается исключительно в качестве второстепенного члена предложения в примерах и пояснениях к ним.


Все верно, но так и у меня нет цели полноценно объявить функцию через friend. Я хочу указать компилятору, что в таком-то классе будет такая-то функция, которой нужно дать доступ к закрытым членам этого класса. Информации, указанной во friend-объявлении, для этого достаточно, а ничего больше и не требуется.

А коли уж Вы взялись буквоедствовать, то еще раз подчеркну, что пространства имен (namespace) в данном случае не используются, так что оба класса вместе со своими методами находятся в одном и том же пространстве имен. Функция, которую я хочу объявить, как дружественную, находится в другой области видимости (scope).

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

S>Вы хотите указать метод того класса, который ещё не определён, как дружественный?


Да.

S>Если этот класс, который с дружесвенным методом, принимать как параметр шаблона


Если нет менее кривого решения, я лучше объявлю дружественным весь класс, хотя пытаюсь этого избежать.
Re[4]: variadic
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 25.09.23 14:23
Оценка:
Здравствуйте, rg45, Вы писали:

R>Его от шаблонов корежит, как вампира от чеснока, он макросы использует, только чтоб к шаблонам не прикасаться.


Я не раз подчеркивал, что регулярно применяю шаблоны по их прямому назначению. А то, что вместо вашей любимой SFINAE-магии мне часто удобнее применить макрос, который и проще, и понятнее, а часто еще и компактнее по коду — звиняйте, уж как есть.
Re[5]: variadic
От: rg45 СССР  
Дата: 25.09.23 14:28
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Я не раз подчеркивал, что регулярно применяю шаблоны по их прямому назначению. А то, что вместо вашей любимой SFINAE-магии мне часто удобнее применить макрос, который и проще, и понятнее, а часто еще и компактнее по коду — звиняйте, уж как есть.


Да-да, я в курсе, ты тут уже демонстрировал свое прямое назначение
Автор: Евгений Музыченко
Дата: 21.01.23
. Вот точно — мартышка и смартфон.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 25.09.2023 14:30 rg45 . Предыдущая версия .
Re[3]: наборы
От: Sm0ke Россия ksi
Дата: 25.09.23 14:34
Оценка:
Здравствуйте, Евгений Музыченко

Это обсуждение привело меня к размышлениям над смежной темой.
А именно об отделдении структур для хранения данных от
набора действий над ними.

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

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

В итоге я написал вот такой простенький пример — один из вариантов — как это можно осуществить.
link: https://godbolt.org/z/q3x7TcfE5
#include <type_traits>
#include <iostream>

struct t_file;
struct t_console;
struct restrict;

template <typename Type>
struct io_actions
{
  using pointer = Type *;

  // data
  pointer self;

  // methods
  void read(restrict & p);
  void write(restrict & p);

  // hint
  io_actions * operator -> () { return this; }
};

struct restrict
{
  friend void io_actions<t_console>::write(restrict &);
  friend void io_actions<t_console>::read(restrict &);
  friend void io_actions<t_file>::write(restrict &);
  friend void io_actions<t_file>::read(restrict &);
private:
  int value{55};
};

struct t_console
{
  using trait = io_actions<t_console>;

  // data
  std::istream * in{& std::cin};
  std::ostream * out{& std::cout};

  trait operator -> () { return {this}; }
};

template <> void t_console::trait::read(restrict & p) { (*self->in) >> p.value; }
template <> void t_console::trait::write(restrict & p) { (*self->out) << p.value; }

int main()
{
  restrict v;
  t_console c;
  c->write(v);
  return 0;
}


Что тут происходит?
В траите io_actions только два метода read() и write(), которые работают с приватными данными из некого класса restrict

Понятно, что в дальнейшем можно определить и дополнительные отдельные траиты (наборы действий), скажем преобразования, проверка, да что угодно
, и оформить их как отдельный nest.

Почему я оформил trait как шаблонный?
Просто в данном случае конкретый обработчик может быть разным (консоль, файл, сеть, ещё куда)
И тут сами конкретные обработчики тоже not_over_bloated тучей кучей методов) а просто что-то хранят

Такая штука работает без интерфейса с виртуальными методами, которые хочется по возможности избежать.

Кстати, в примере выше нет definition для класса t_file
Вроде не сложно его сделать по анологии, если он вообще нужен
А действия тогда будут заданы как-то так:
template <> void t_file::trait::read(restrict & p) { /* ... */ }
template <> void t_file::trait::write(restrict & p) { /* ... */ }


p/s
Оператор стрелка у t_console создаёт временный объект-wrapper, это вообще-то под вопросом — как лучше можно оформить)

upd
По поводу iostream: я просто старался чтобы пример был понятным
Отредактировано 25.09.2023 14:40 Sm0ke . Предыдущая версия . Еще …
Отредактировано 25.09.2023 14:38 Sm0ke . Предыдущая версия .
Отредактировано 25.09.2023 14:37 Sm0ke . Предыдущая версия .
Отредактировано 25.09.2023 14:37 Sm0ke . Предыдущая версия .
Re[9]: Почему нельзя предварительно объявить дружественную ф
От: so5team https://stiffstream.com
Дата: 25.09.23 15:04
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

S>>Если ваш код не соответствует стандарту, то он запросто может перестать работать при обновлении компилятора.


ЕМ>Теоретически — может. А практически — ни разу не переставал. Он даже компилироваться ни разу не переставал — только новые предупреждения появлялись при /Wall.


Так это потому, что вы на компиляторах от одного производителя сидите.

ЕМ>Вот только практика показывает, что код, полностью соответствующий определенному стандарту, запросто может перестать компилироваться и/или работать при обновлении компилятора, ориентированного на более новые стандарты, и не поддерживающего старых (по крайней мере, по умолчанию). С этим что будем делать?


Если старый код не компилируется в рамках нового стандарта, то это вполне ожидаемо.
Выхода два:

— либо вы адаптируете код к новому стандарту;
— либо вы явно задаете стандарт ключиками компилятора. Ведь даже MS VC++ научили понимать /std.

ЕМ>Вообще, требование полного соответствия любого кода стандарту — это, грубо говоря, как требование надежной работы изделия в пределах хотя бы всей планеты — и на экваторе, и на полюсах, и в стратосфере, и под водой.


Бла-бла-бла.

ЕМ>Это не доблесть и не бравада. Это вполне разумный инженерный подход, который еще не так давно преобладал и в C, и в C++, а сейчас продолжает преобладать в большинстве других языков, для которых нет единых стандартов. А вот в C++ из стандарта сделали фетиш.


В мире не так-то много языков, у которых: а) есть международный стандарт и b) есть независимые его реализации от разных производителей.

Повторюсь, вам не приходилось переносить даже нормально написанный код между разными C++ компиляторами, поэтому вам кажется, что требование следовать стандарту C++ -- это фетиш. А вот тем, кому доводилось, понимают, что следование стандарту -- это хоть какая-то гарантия, что код хотя бы соберется на другом компиляторе.
Re[3]: Почему нельзя предварительно объявить дружественную функ
От: so5team https://stiffstream.com
Дата: 25.09.23 15:20
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

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


Какие-то странные поиски того, чего нет, когда можно решить "в лоб":
class A;
class B;

class A {
  friend void modify_A(A &, B &);
  ...

  void f(B & consumer);
};

class B {
  friend void modify_A(A &, B &);

public:
  ...
  void consume(A & a) {
    modify_A(a, *this);
  }
};

void A::f(B & consumer) {
  consumer.consume(*this);
}

void modify_A(A & a, B & b) {
  // Тут есть доступ и к потрохам A, и к потрохам B.
}
Re[10]: Почему нельзя предварительно объявить дружественную ф
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 25.09.23 17:00
Оценка:
Здравствуйте, so5team, Вы писали:

S>Так это потому, что вы на компиляторах от одного производителя сидите.


Что толку сидеть на разных, когда нужен результат в виде работающего двоичного кода, а не "ультрасовместимый" исходник?

S>В мире не так-то много языков, у которых: а) есть международный стандарт и b) есть независимые его реализации от разных производителей.


S>вам не приходилось переносить даже нормально написанный код между разными C++ компиляторами


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

S>поэтому вам кажется, что требование следовать стандарту C++ -- это фетиш. А вот тем, кому доводилось, понимают, что следование стандарту -- это хоть какая-то гарантия, что код хотя бы соберется на другом компиляторе.


Там, где или первичен исходник (open source и подобное), или не могут жить без регулярных обновлений компиляторов "просто потому, что выпустили новый", необходимо следовать стандарту. Там, где первичен бинарник, а компилятор меняют не раньше, чем упрутся в нерешаемую имеющимся компилятором проблему, следование стандарту не столь важно, сколь получение предсказуемого кода.
Re[4]: Почему нельзя предварительно объявить дружественную функ
От: Sm0ke Россия ksi
Дата: 25.09.23 18:02
Оценка:
Здравствуйте, so5team, Вы писали:

S>Какие-то странные поиски того, чего нет, когда можно решить "в лоб":

S>
S>class A;
S>class B;

S>class A {
S>  friend void modify_A(A &, B &);
S>  ...

S>  void f(B & consumer);
S>};

S>class B {
S>  friend void modify_A(A &, B &);

S>public:
S>  ...
S>  void consume(A & a) {
S>    modify_A(a, *this);
S>  }
S>};

S>void A::f(B & consumer) {
S>  consumer.consume(*this);
S>}

S>void modify_A(A & a, B & b) {
S>  // Тут есть доступ и к потрохам A, и к потрохам B.
S>}
S>


Это обходные пути. Что, для каждого метода делать обёртку? А если их много — всё это ручками писать?

  cut
А может даже кодогенерировать? Если простые штуки в языке решаются через кодогенерацию, то стоит задуматься
Re[11]: Почему нельзя предварительно объявить дружественную ф
От: so5team https://stiffstream.com
Дата: 25.09.23 19:10
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

S>>Так это потому, что вы на компиляторах от одного производителя сидите.


ЕМ>Что толку сидеть на разных, когда нужен результат в виде работающего двоичного кода, а не "ультрасовместимый" исходник?


Тем, что в мире есть более одной платформы. Как программной, так и аппаратной.

S>>В мире не так-то много языков, у которых: а) есть международный стандарт и b) есть независимые его реализации от разных производителей.


S>>вам не приходилось переносить даже нормально написанный код между разными C++ компиляторами


ЕМ>Таки приходилось.


Судя по тому, что вы пишете, перенос был с VC++ 4.2 на VC++ 6.0.
Re[5]: Почему нельзя предварительно объявить дружественную функ
От: so5team https://stiffstream.com
Дата: 25.09.23 19:16
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Чем это лучше, как сделать вспомогательную функцию открытым членом класса A, и вызывать ее из класса B?


Тем, что тогда ее сможет вызвать кто угодно, а не только класс B. Ваш К.О.

ЕМ>"В лоб" — это объявить дружественным весь класс B, и забить на принцип минимальных прав.


Я так понял, что решается следующая задача:

— нужно минимизировать объем кода, который имеет доступ к потрохам класса A (т.е. до одной функции/метода);
— сделать это нужно в условиях, когда перед определением класса A нельзя сделать определение класса B (которому и нужен доступ к потрохам B).

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

S>в мире есть более одной платформы. Как программной, так и аппаратной.


Какова доля "истинно кросс-платформенного" кода на C++ во всем объеме кода на C++? А если вычесть формально существующий, но не используемый сколько-нибудь активно?

Чем дальше задача уходит от конкретной платформы, тем меньше остается оснований делать ее на C++.
Re[7]: Почему нельзя предварительно объявить дружественную функ
От: Sm0ke Россия ksi
Дата: 25.09.23 21:23
Оценка:
Здравствуйте, Евгений Музыченко, как думаете, когда-нибудь требование для forward declaration в языке си++ станут необязательными?
т.е. если некие имена видны _вконце_ единици трансляции, то все эти имена и будут видны из любой точки этой же единици трансляции.

А может быть уже есть я.п. в которых это реализовано?
Re[6]: read_only ячейка
От: Sm0ke Россия ksi
Дата: 25.09.23 21:38
Оценка:
Здравствуйте, so5team, Вы писали:

S>Если у класса A слишком много друзей, то с классом A явно что-то не так.


Надо ли перечислять друзей, Когда всё public: ?

--

Серьёзно: Если бы было ...
public read_only protected full_access:
... то число геттеров могло бы поубавиться
Отредактировано 25.09.2023 21:42 Sm0ke . Предыдущая версия .
Re[13]: Почему нельзя предварительно объявить дружественную ф
От: so5team https://stiffstream.com
Дата: 26.09.23 05:03
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

S>>в мире есть более одной платформы. Как программной, так и аппаратной.


ЕМ>Какова доля "истинно кросс-платформенного" кода на C++ во всем объеме кода на C++?


ХЗ. В моей около тридцатилетней практике что-то около 100%. Включая ситуации, когда доступ к целевой платформе ограничен (условный SPARKstation один, а коллектив большой, и доступ к нему давался по расписанию) и большая часть разработки велась на x86 Linux с gcc. И ситуации, когда приложение разрабатывалось без планов портирования куда-либо, а потом возникала идея "вот есть такая аппаратно-программная платформа, если мы на нее портируем свой софт, то сможем заходить к потенциальным клиентам и со стороны продавцов этой платформы".

Но, подозреваю, что когда человек пишет драйвера для Windows на x86/x64, то весь мир C++ для него выглядит как прибитый гвоздями для одной платформы.

ЕМ>А если вычесть формально существующий, но не используемый сколько-нибудь активно?


Не распарсил.

ЕМ>Чем дальше задача уходит от конкретной платформы, тем меньше остается оснований делать ее на C++.


Ну да, ну да. Продавцы Qt смотрят на вас как на...
Re[7]: read_only ячейка
От: so5team https://stiffstream.com
Дата: 26.09.23 05:25
Оценка:
Здравствуйте, Sm0ke, Вы писали:

S>>Если у класса A слишком много друзей, то с классом A явно что-то не так.


S>Серьёзно: Если бы было ...
S>public read_only protected full_access:
S>
... то число геттеров могло бы поубавиться


Тут нужно смотреть по конкретной ситуации, а не по сферическим примерам в вакууме.
Из собственной практики могу вспомнить такой пример.
Есть у нас класс environment_t, который дает пользователю доступ к ряду публичных методов. Но внутри библиотеки нужно уметь обращаться и к закрытой части функциональности этого класса. Причем из разных мест библиотеки.

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

Поэтому проще объявить для environment_t другом отдельный внутренний класс internal_env_iface_t, который живет в специальном внутреннем пространстве имен и не предназначен для прямого использования конечным пользователем.

Конечно, в C++ нет средств полностью ограничить доступ к internal_env_iface_t. Но при уже имеющейся сложности языка я не знаю, хотел бы иметь еще и такие возможности. ИМХО, прагматичнее просто сказать "вот это руками не трогать", а кто не внял, тот ССЗБ.
Re[8]: Почему нельзя предварительно объявить дружественную функ
От: so5team https://stiffstream.com
Дата: 26.09.23 07:07
Оценка:
Здравствуйте, Sm0ke, Вы писали:

S>А может быть уже есть я.п. в которых это реализовано?


Java, например:
public class MyClass {
    public static void main(String args[]) {
        var x = new Helper();
        var y = new Helper();

        x.add(2);
        x.mul(10);
        
        y.add(3);
        y.mul(5);

      System.out.println("Sum of x+y = " + (x.current() + y.current()) );
    }
}

class Helper {
    private int x = 0;
    public void add(int d) {
        x += d;
    }
    public void mul(int d) {
        x *= d;
    }
    public int current() { return x; }
}

В MyClass использование класса Helper и его методов начинается до того, как сам класс Helper будет объявлен. Насколько я помню, это действительно в Java для классов с областью видимости package.
Re[8]: Почему нельзя предварительно объявить дружественную функ
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 26.09.23 07:23
Оценка:
Здравствуйте, Sm0ke, Вы писали:

S>как думаете, когда-нибудь требование для forward declaration в языке си++ станут необязательными?


Я даже думать не хочу о таком ужасе. Хватит и того, что в угоду вполне себе глупой концепции "interface first" добавили возможность определять данные класса после методов. Представляю, сколько это вызвало (и продолжает вызывать) матов у разработчиков компиляторов.
Re[7]: read_only ячейка
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 26.09.23 07:25
Оценка:
Здравствуйте, Sm0ke, Вы писали:

S>Если бы было

S>
S>public read_only protected full_access:
S>


Я обеими руками за readonly, чтобы не плодить тривиальных геттеров. А какой смысл в full_access при наличии public?
Re[8]: Почему нельзя предварительно объявить дружественную функ
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 26.09.23 07:47
Оценка:
Здравствуйте, so5team, Вы писали:

ЕМ>>кто из перечисленных не сможет вызвать функцию modify_A?


S>Например тот, кто не имеет валидной ссылки на экземпляр B.


Тот, кто не имеет экземпляра класса или указателя/ссылки на него, не сможет ничего сделать данными класса. Вытекает ли из этого, что private не имеет смысла?

S>""В лоб" — это объявить дружественным весь класс B, и забить на принцип минимальных прав." -- не есть решение поставленной задачи.


Это частное решение поставленной задаче с минимумом извращений. Городить ради этого невнятную обвязку, смысла которой я через год сам не пойму без подробных комментариев — нунах. У меня уже применяется несколько подобных кривых "решений", от которых до сих пор неуютно.

S>Точнее было бы сказать, что это вы не видите принципиальных препятствий.


А Вы видите? Если да, то в чем их принципиальность?

S>Вам ув.тов.watchmaker уже описал некоторые причины


Товарищу я давно ответил, что описанные им причины никак не относятся к обсуждаемому вопросу.

S>означает, что нужно ввести еще одно состояние. Нечто вроде частично определенный класс.


Не нужно. На степень определенности класса это вообще никак не повлияет.

S>при уже имеющейся сложности языка я не знаю, хотел бы иметь еще и такие возможности


Если б сложность языка диктовалась только необходимостью... Когда-то все данные было необходимо или определять, или объявлять до их использования. Затем это требование сняли для определений классов. Какие реальные проблемы это решило? А теперь прикиньте, насколько для этого пришлось усложнить спецификацию и реализацию компилятора.
Re[15]: Почему нельзя предварительно объявить дружественную ф
От: so5team https://stiffstream.com
Дата: 26.09.23 08:47
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>>>Какова доля "истинно кросс-платформенного" кода на C++ во всем объеме кода на C++?


S>>ХЗ. В моей около тридцатилетней практике что-то около 100%.


ЕМ>Это наводит на мысль о том, что Вы работаете преимущественно в сфере кросс-платформенного опенсорса.


Разве что последние 10 лет, и то не полностью.

Однако, к чему это? Вы пытаетесь доказать, что подавляющая часть C++ного кода изначально прибита гвоздями к одной платформе и одной версии одного компилятора?

S>>подозреваю, что когда человек пишет драйвера для Windows на x86/x64, то весь мир C++ для него выглядит как прибитый гвоздями для одной платформы.


ЕМ>Человек говорит о том, что кросс-платформенность не является (и не должна быть) сверхцелью. Это лишь одно из свойств ПО, но далеко не всегда ключевое, и далеко не всегда даже желательное.


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

ЕМ>Огромное количество софта, особенно опенсорсного, существует лишь потому, что его просто захотели написать — так же, как литература, поэзия, живопись и видосики.


И? Вот Chrome построен на базе открытого ПО. Можно ли представить себе современный Интернет без Chrome и разработок на его основе?

ЕМ>>>Чем дальше задача уходит от конкретной платформы, тем меньше остается оснований делать ее на C++.


S>>Продавцы Qt смотрят на вас как на...


ЕМ>Еще скажите, что по мере развития Qt разработчики массово бросают Java, Python и подобное, и переходят на C++.


Вы странно интерпретируете события: массовый отказ от C++ начался более 25 лет назад. Обычные компьютеры обычных пользователей перестали тормозить уже лет 20 как, так что использование софта на Java/C#/Python/Electron/... уже не представляет проблемы для рядового пользователя. Но Qt все еще продается.

Возможно, со временем Rust отберет у C++ изрядную долю оставшегося у C++ рынка. Но пока это еще не произошло.

Так что писать в user-space на C++ есть еще достаточно резонов.
Re[9]: Почему нельзя предварительно объявить дружественную функ
От: so5team https://stiffstream.com
Дата: 26.09.23 09:04
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>>>кто из перечисленных не сможет вызвать функцию modify_A?


S>>Например тот, кто не имеет валидной ссылки на экземпляр B.


ЕМ>Тот, кто не имеет экземпляра класса или указателя/ссылки на него, не сможет ничего сделать данными класса. Вытекает ли из этого, что private не имеет смысла?


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

S>>""В лоб" — это объявить дружественным весь класс B, и забить на принцип минимальных прав." -- не есть решение поставленной задачи.


ЕМ>Это частное решение поставленной задаче с минимумом извращений.


Сделать весь класс B другом для A не есть решение задачи, в постановке которой присутствует "нужно минимизировать объем кода, который имеет доступ к потрохам класса A (т.е. до одной функции/метода)". Вы вместо поиска решения меняете задачу.

S>>Точнее было бы сказать, что это вы не видите принципиальных препятствий.


ЕМ>А Вы видите? Если да, то в чем их принципиальность?


Выше уже было сказано и watchmaker-ом, и мной. Тот факт, что объявляя метод другом без объявления класса B нам придется в этом объявлении указывать виртуальность этого метода (или указывать, что это static-метод). Тот факт, что у нас появляется понятие частично определенного класса.

Это все принципиально важные моменты чтобы послать подобную идею в /dev/null.

ЕМ>Товарищу я давно ответил, что описанные им причины никак не относятся к обсуждаемому вопросу.


Так я и говорю, вам ужасная правда нужна, простой вам недостаточно.

S>>означает, что нужно ввести еще одно состояние. Нечто вроде частично определенный класс.


ЕМ>Не нужно. На степень определенности класса это вообще никак не повлияет.


Правда?
class B;

class A {
  friend void B::f(int a) const;
  ...
};

void g(B & b) // Это допустимо и без полного определения B.
{
  b.f(0); // Ведь мы знаем про наличие f(int) в B.
  ...
}


Все еще никак не влияет?

S>>при уже имеющейся сложности языка я не знаю, хотел бы иметь еще и такие возможности


ЕМ>Если б сложность языка диктовалась только необходимостью... Когда-то все данные было необходимо или определять, или объявлять до их использования.


Разве что во времена C, когда C++ еще не было.

ЕМ>Затем это требование сняли для определений классов. Какие реальные проблемы это решило?


Например, вот такие:
class Demo {
  void f() {
    ...
    this->g();
  }
  void g() {
    ...
  }
  ...
};

Demo::f() может вызывать Demo::g() еще до объявления g(). Выйти из этого можно было бы так:
class Demo {
  void f(); // Никакого inline в определении класса.
  void g(); // Никакого inline в определении класса.
  ...
};
void Demo::f() {
  this->g(); // Теперь OK, т.к. определение класса было выше.
}
void Demo::g() {
  ...
}

что сделало бы использование С++ менее удобным.

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

S>Сделать весь класс B другом для A не есть решение задачи, в постановке которой присутствует "нужно минимизировать объем кода, который имеет доступ к потрохам класса A (т.е. до одной функции/метода)". Вы вместо поиска решения меняете задачу.


Коню понятно, что упомянутая задача не является ни первичной, ни даже необходимой. Первичная задача — обеспечить разумную степень безопасности кода, она и не меняется. А конкретный элемент этой задачи — минимизация кода с повышенными правами — в данном случае приходится признать не реализуемым адекватными методами. Неадекватных же я применять не хочу, они того не стоят.

S>объявляя метод другом без объявления класса B нам придется в этом объявлении указывать виртуальность этого метода (или указывать, что это static-метод).


Зачем? Это все относится сугубо к вызову функции, в котором здесь необходимости не возникает. А если бы и требовалось, то в чем проблема? Коли уж сложилась ситуация, когда во friend недостаточно указать только имя, то какая печаль с того, что прототип придется указывать со всеми атрибутами?

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


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

С чего бы вдруг

S> friend void B::f(int a) const;


стало достаточным для

S> b.f(0);


? Кто поставил такую задачу? Я ее не ставил.

ЕМ>>Когда-то все данные было необходимо или определять, или объявлять до их использования.


S>Разве что во времена C, когда C++ еще не было.


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

S> void f(); // Никакого inline в определении класса.


Почему никакого? Ничто не мешало использовать inline в объявлении функции, в качестве атрибута "реализацию этой функции желательно инлайнить". Все равно эти вопросы решаются генератором кода, который может работать, как ему удобно.
Re[11]: Почему нельзя предварительно объявить дружественную функ
От: so5team https://stiffstream.com
Дата: 26.09.23 11:23
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

S>>Сделать весь класс B другом для A не есть решение задачи, в постановке которой присутствует "нужно минимизировать объем кода, который имеет доступ к потрохам класса A (т.е. до одной функции/метода)". Вы вместо поиска решения меняете задачу.


ЕМ>Коню понятно, что упомянутая задача не является ни первичной, ни даже необходимой.


Евгений, ну тут же все ходы записаны:

Я так понял, что решается следующая задача:

— нужно минимизировать объем кода, который имеет доступ к потрохам класса A (т.е. до одной функции/метода);
— сделать это нужно в условиях, когда перед определением класса A нельзя сделать определение класса B (которому и нужен доступ к потрохам B).


Что вы и подтверждаете:

Верно.


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

S>>объявляя метод другом без объявления класса B нам придется в этом объявлении указывать виртуальность этого метода (или указывать, что это static-метод).


ЕМ>Зачем? Это все относится сугубо к вызову функции, в котором здесь необходимости не возникает. А если бы и требовалось, то в чем проблема? Коли уж сложилась ситуация, когда во friend недостаточно указать только имя, то какая печаль с того, что прототип придется указывать со всеми атрибутами?


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

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


ЕМ>Я уже говорил, что не появится, нет такой необходимости.


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

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


S>> friend void B::f(int a) const;


ЕМ>стало достаточным для


S>> b.f(0);


ЕМ>?


Да потому, что это согласуется вот с таким, например (цинк):
#include <utility>
#include <iostream>

class Demo {
    int m_a;

    friend void swap(Demo & a, Demo & b) noexcept;

    friend std::ostream & operator<<(std::ostream & to, const Demo & v);

public:
    Demo() : m_a{42} {}

    void change(int v) { m_a = v; }
};

int main() {
    Demo a, b;
    std::cout << a << b << std::endl;
    a.change(50);
    b.change(60);
    std::cout << a << b << std::endl;
    swap(a, b);
    std::cout << a << b << std::endl;
}

void swap(Demo & a, Demo & b) noexcept {
    using std::swap;
    swap(a.m_a, b.m_a);
}

std::ostream & operator<<(std::ostream & to, const Demo & v) {
    return (to << v.m_a);
}

Обратите внимания, что вызовы swap и operator<< для экземпляров Demo стали возможны потому, что в определении Demo для них есть декларации (но определения даны уже после использования внутри main).

ЕМ>Кто поставил такую задачу?


Это естественным образом проистекает из того, что мы начинаем делать частичное описание класса.

ЕМ>>>Когда-то все данные было необходимо или определять, или объявлять до их использования.


S>>Разве что во времена C, когда C++ еще не было.


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


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

S>> void f(); // Никакого inline в определении класса.


ЕМ>Почему никакого?


Потому, что написано что inline в определении класса.
Re[8]: private nested class
От: Sm0ke Россия ksi
Дата: 26.09.23 11:28
Оценка:
Здравствуйте, so5team, Вы писали:

S>Конечно, в C++ нет средств полностью ограничить доступ к internal_env_iface_t. Но при уже имеющейся сложности языка я не знаю, хотел бы иметь еще и такие возможности. ИМХО, прагматичнее просто сказать "вот это руками не трогать", а кто не внял, тот ССЗБ.


Привэйт нэстед класс

struct nest
{
private:
  struct hidden {};
};

int main()
{
  nest::hidden v; // error C2248: 'nest::hidden': cannot access private struct declared in class 'nest'
  return 0;
}


Also: Out of class nested struct definition

struct nest
{
private:
  struct hidden_1;
  struct hidden_2;
  struct hidden_3;

public:
  static void fn_test();
};

struct nest::hidden_1 {};
struct nest::hidden_2 { hidden_1 data; };
struct nest::hidden_3 {};

void nest::fn_test()
{
  hidden_2 obrabotchik;
}

int main()
{
  nest::fn_test();
  return 0;
}


Keyword 'friend' was used zero times in samples above
Re[9]: private nested class
От: so5team https://stiffstream.com
Дата: 26.09.23 12:46
Оценка:
Здравствуйте, Sm0ke, Вы писали:

S>>Конечно, в C++ нет средств полностью ограничить доступ к internal_env_iface_t. Но при уже имеющейся сложности языка я не знаю, хотел бы иметь еще и такие возможности. ИМХО, прагматичнее просто сказать "вот это руками не трогать", а кто не внял, тот ССЗБ.


S>Привэйт нэстед класс

S>Also: Out of class nested struct definition
S>Keyword 'friend' was used zero times in samples above

Не уверен, что вы поняли меня, но уверен, что не понял вас.
Re[10]: private nested class
От: Sm0ke Россия ksi
Дата: 26.09.23 14:04
Оценка:
Здравствуйте, so5team, Вы писали:

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


Это был мой ответ на Часть вашего сообщения:

Есть у нас класс environment_t, который дает пользователю доступ к ряду публичных методов. Но внутри библиотеки нужно уметь обращаться и к закрытой части функциональности этого класса. Причем из разных мест библиотеки.

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

Поэтому проще объявить для environment_t другом отдельный внутренний класс internal_env_iface_t, который живет в специальном внутреннем пространстве имен и не предназначен для прямого использования конечным пользователем.


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

Далее вы писали:

Конечно, в C++ нет средств полностью ограничить доступ к internal_env_iface_t.


В си++ есть такая возможность. В этом может помочь nested classes
https://en.cppreference.com/w/cpp/language/nested_types
Re[11]: private nested class
От: so5team https://stiffstream.com
Дата: 26.09.23 14:52
Оценка:
Здравствуйте, 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

Вот это и заставляет меня думать, что вы не поняли меня.
Если делать internal_env_iface_t закрытым, то чтобы дать к нему доступ из разных мест придется эти разные места прописывать в качестве друзей. А именно этого и хочется избежать.
Re[5]: Почему нельзя предварительно объявить дружественную функ
От: B0FEE664  
Дата: 26.09.23 15:28
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

S>>Какие-то странные поиски того, чего нет, когда можно решить "в лоб":

ЕМ>Это ж тоже кривой костыль. Чем это лучше, как сделать вспомогательную функцию открытым членом класса A, и вызывать ее из класса B?

Судя по этому замечанию, вы задаёте не тот вопрос, на который хотите получить ответ. Правильно ли я понимаю, что вы хотите объявить другом класса закрытый (приватный) метод другого класса?
И каждый день — без права на ошибку...
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[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[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...
Пока на собственное сообщение не было ответов, его можно удалить.