Язык допускает предварительное объявление (forward declaration) дружественной (friend) обычной (non-member) функции, но почему-то не допускает такого для функции-члена, класс которой не определен полностью (только предварительно объявлен — "class C;").
Откуда растет это ограничение? Вроде как предварительная декларация функции-члена другого класса обеспечивает компилятор всей информацией об этой функции (по сути, ему должно быть достаточно сигнатуры). Чего здесь не хватает компилятору?
Re: Почему нельзя предварительно объявить дружественную функцию-член?
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Вроде как предварительная декларация функции-члена другого класса обеспечивает компилятор всей информацией об этой функции (по сути, ему должно быть достаточно сигнатуры).
Кажется, это совсем не так.
Декларация обычной функции позволяет компилятору генерировать код её вызова. Собственно, на этом разделная компиляция с header-файлами и построена.
Но для функции-члена у тебя может быть разный код вызова для виртуального метода и для невиртуального. То есть для классов
struct S {
void foo(int);
};
struct B {
virtual void 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: Почему нельзя предварительно объявить дружественную функ
struct A
{
friend void foo();
};
int main()
{
foo(); // error: 'foo' was not declared in this scope
}
Язык допускает объявление дружественной функции без ее предварительного объявления только для функций из того же пространства имен, что и сам класс. А для того, чтобы объявить дружественной функцию из другого пространства имен, ее нужно будет сначала объявить. И сделать это нужно будет в пространстве имен функции.
Здравствуйте, watchmaker, Вы писали:
W>Декларация обычной функции позволяет компилятору генерировать код её вызова.
Спасибо за развернутые рассуждения, но при чем они здесь? Если я в классе A указываю дружественную функцию из класса B, это сообщает компилятору лишь то, что эта функция будет иметь право доступа к закрытым членам класса A, и ничего больше. Если бы компилятор выдавал ошибку в точке вызова этой функции, пока ее класс полностью не определен, я бы не удивился. Но он выдает ошибку именно в точке объявления friend, и это странно.
Re[2]: Почему нельзя предварительно объявить дружественную функ
Здравствуйте, rg45, Вы писали:
R>И сходу неправильно.
Точнее, Вы с ходу стали искать, к чему прицепиться, и таки нашли.
R>Объявление функции дружественной не является предварительным объявлением функции
Само собой. Где я утверждал обратное?
R>Язык допускает объявление дружественной функции без ее предварительного объявления только для функций из того же пространства имен, что и сам класс.
Не понял, при чем здесь пространства имен. Если класс, членом которого является дружественная функция, полностью определен раньше класса, который указывает ее в качестве дружественной, то все компилируется, хотя все имена остаются в тех же пространствах.
R>для того, чтобы объявить дружественной функцию из другого пространства имен, ее нужно будет сначала объявить. И сделать это нужно будет в пространстве имен функции.
Мне нужно указать в качестве дружественной функцию, которая является членом класса, а не просто пространства имен.
R>если ты потрудишься переформулировать свой вопрос без использования ошибочных утверждений
Вы твердо уверены в том, что ошибочные утверждения делаю именно я?
Re[3]: Почему нельзя предварительно объявить дружественную ф
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Мне нужно указать в качестве дружественной функцию, которая является членом класса, а не просто пространства имен.
Блин. Ну ты же апеллируешь к тому, что свободные функции, якобы, можно объявлять дружественными без предварительного объявления. Вот я тебе и показываю, что твои апелляции безосновательны, потому что, в общем случае, свободную функцию тоже сначала нужно объявить, прежде чем ее можно будет объявить дружественной:
namespace ns
{
void foo();
} // namespace nsstruct A
{
friend void ns::foo(); // Okfriend void ns::bar(); // error: 'void ns::bar()' should have been declared inside 'ns'
};
Исключением являются только дружественные функции из того же пространства имен, что и сам класс. Нужно объяснять, почему подобное исключение невозможно сделать для дружественных функций-членов, или сам напряжешь извилину?
Здравствуйте, Евгений Музыченко, Вы писали:
R>>Объявление функции дружественной не является предварительным объявлением функции
ЕМ>Само собой. Где я утверждал обратное?
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>А чем это обосновано? ЕМ>Понятно, что при условии вышеприведенного правила это невозможно. А для чего необходимо само правило?
Вот видишь, ты уже пытаешься переформулировать вопрос, это уже хорошо. Ты не торопись, подумай, глядишь, вопрос-то и отвалится, как дурацкий.
P.S. Что именно тебя удивляет — что функции из одних пространств имен нельзя объявлять в других?
Я утверждал именно то, что в обычной практике (без разделения на пространства имен) он это допускает.
Осталось понять, почему не допускает это для функции-члена. Если объявить заранее пространство имен нельзя (тоже, кстати, искусственное ограничение), то класс-то заранее объявить можно.
Re[5]: Почему нельзя предварительно объявить дружественную ф
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Я утверждал именно то, что в обычной практике (без разделения на пространства имен) он это допускает. ЕМ>Осталось понять, почему не допускает это для функции-члена. Если объявить заранее пространство имен нельзя (тоже, кстати, искусственное ограничение), то класс-то заранее объявить можно.
Да у тебя на каждом шагу "исскусственные ограничения". Тебе самому не надоело жить в выдуманном мире? Подучил бы уже язык, с которым работаешь, хотя бы на уровне "объявления-определения" да "аз-буки".
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Я утверждал именно то, что в обычной практике (без разделения на пространства имен) он это допускает. ЕМ>Осталось понять, почему не допускает это для функции-члена. Если объявить заранее пространство имен нельзя (тоже, кстати, искусственное ограничение), то класс-то заранее объявить можно.
И кстати, стандарт языка не вводит термина "forward declaration" как такового. Есть "declaration" и есть "definition", а слово forward встречается исключительно в качестве второстепенного члена предложения в примерах и пояснениях к ним.
--
Re[5]: Почему нельзя предварительно объявить дружественную ф
Вы хотите указать метод того класса, который ещё не определён, как дружественный?
Если этот класс, который с дружесвенным методом, принимать как параметр шаблона, то это может получится
Если имя метода, который вы хотите указать как friend, сделать зависимым от шаблонного параметра, то
разрешение имён откладывается до мемента инстанцирования.
Про это есть видео на ютуб канале Константина Владимирова
Магистерский курс C++ (МФТИ, 2022-2023). Лекция 4. Разрешение имён в шаблонах и One Definition Rule.
Здравствуйте, rg45, Вы писали:
R>Тебе самому не надоело жить в выдуманном мире?
Это еще большой вопрос, у кого мир более выдуманный. Я-то языком пользуюсь сугубо прагматично, для получения кода, надежно работающего в пределах заданных допущений, и не претендующего ни на полное соответствие Стандарту, ни на "каноничность". Так уж сделали язык, что для этого его регулярно приходится гнуть об колено. А Вы вместе с теми, кто меня этим попрекает, ратуете за рафинированную корректность и чуть ли не математическую чистоту. Кто из нас ближе к реальности?
R>Подучил бы уже язык, с которым работаешь, хотя бы на уровне "объявления-определения" да "аз-буки".
На уровне "для работы" я его понимаю гораздо лучше среднего заурядного плюсовика, который тупо кодит алгоритмы по заданию. Изучать же на уровне, достаточном для сдачи экзамена или преподавания, не вижу ни малейшего смысла, ибо результатов моей работы это ни разу не улучшит.
Re[6]: Почему нельзя предварительно объявить дружественную ф
Здравствуйте, rg45, Вы писали:
R>стандарт языка не вводит термина "forward declaration" как такового. Есть "declaration" и есть "definition", а слово forward встречается исключительно в качестве второстепенного члена предложения в примерах и пояснениях к ним.
Все верно, но так и у меня нет цели полноценно объявить функцию через friend. Я хочу указать компилятору, что в таком-то классе будет такая-то функция, которой нужно дать доступ к закрытым членам этого класса. Информации, указанной во friend-объявлении, для этого достаточно, а ничего больше и не требуется.
А коли уж Вы взялись буквоедствовать, то еще раз подчеркну, что пространства имен (namespace) в данном случае не используются, так что оба класса вместе со своими методами находятся в одном и том же пространстве имен. Функция, которую я хочу объявить, как дружественную, находится в другой области видимости (scope).
В обоих случаях не вижу никаких принципиальных препятствий для компилятора принимать имя функции с квалификатором области видимости ее класса.
Re[2]: Почему нельзя предварительно объявить дружественную функ
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Я-то языком пользуюсь сугубо прагматично, для получения кода, надежно работающего в пределах заданных допущений, и не претендующего ни на полное соответствие Стандарту, ни на "каноничность".
Если ваш код не соответствует стандарту, то он запросто может перестать работать при обновлении компилятора.
Да, да, тут уже все знают, что это не ваш случай и что у вас компиляторов этих всего 1-2 и обчелся, да к тому же все они от Microsoft.
Но доблести, увы, в этом нет. И у людей, которые набили шишек при переносе кода между vc++, gcc и clang (хотя бы этими тремя), подобная бравада одобрения не вызывает, мягко говоря.
Re[7]: Почему нельзя предварительно объявить дружественную ф
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Это еще большой вопрос, у кого мир более выдуманный. Я-то языком пользуюсь сугубо прагматично, для получения кода, надежно работающего в пределах заданных допущений, и не претендующего ни на полное соответствие Стандарту, ни на "каноничность". Так уж сделали язык, что для этого его регулярно приходится гнуть об колено. А Вы вместе с теми, кто меня этим попрекает, ратуете за рафинированную корректность и чуть ли не математическую чистоту. Кто из нас ближе к реальности?
Да какое там, нафиг, полное соответствие, когда ты азов не знаешь. Невежество на невежестве, так еще и гордится этим. Языком он пользуется. Как шимпанзе смартфоном.