Почему в С++ не существует ссылки на член?

В C++ я могу выбирать между указателями функций и ссылками на функции (или даже значениями функций для полноты картины):

void call_function_pointer (void (*function)()) {
    (*function) ();
}
void call_function_reference (void (&function)()) {
    function ();
}
void call_function_value (void function()) {
    function ();
}

Однако когда дело доходит до методов, у меня, похоже, нет выбора между указателями и ссылками.

template <class T> void call_method_pointer (T* object, void (T::*method)()) {
    (object->*method) ();
}
// the following code creates a compile error
template <class T> void call_method_reference (T& object, void (T::&method)()) {
    object.method ();
}

Это приводит меня к предположению, что в C++ не существует ссылок на методы. Это правда? Если да, то по какой причине их нет?


person eyelash    schedule 22.02.2014    source источник
comment
Вы правы, что ссылок на методы не существует. Стандарт С++ не определяет такой тип и явно указывает (в ненормативной заметке), когда он описывает типы указателя на член, в С++ нет типа ссылки на член. Но я не могу ответить на ваш вопрос о том, почему их не существует.   -  person    schedule 22.02.2014
comment
возможный дубликат ссылки на функцию-член?   -  person Etherealone    schedule 22.02.2014
comment
@Etherealone Почти дубликат, за исключением того, что этот вопрос добавляет дополнительное почему?   -  person Jason C    schedule 22.02.2014
comment
Поскольку методы принадлежат экземпляру, нет смысла передавать их как указатели.   -  person Joatin    schedule 22.02.2014
comment
@Tintin Да, это так (и это просто указатели на записи vtable). Это именно то, для чего нужны операторы указателя на член .* и ->*. Вопрос здесь в том, почему нет эквивалентов ссылки на член, хотя ссылки на объекты и ссылки на функции существуют. Даже на c.l.c++ существует множество дискуссий, которые заходят в тупик, как только кто-то спрашивает, почему. Мне лично очень любопытно обоснование, я уверен, что оно где-то похоронено в некоторых черновиках комментариев.   -  person Jason C    schedule 22.02.2014
comment
Вы можете думать об объекте как об указателе на таблицу методов-членов. Почему вы хотите получить ссылку (или указатель) на один из методов-членов, когда у вас уже есть указатель на таблицу? Вам нужно будет каким-то образом передать this методу, чтобы вы могли использовать его напрямую. Это, вероятно, не тот способ, которым вы хотите создать абстракцию/согласованный интерфейс для нескольких классов (я думаю, это то, что вы пытаетесь). Используйте наследование.   -  person Etherealone    schedule 22.02.2014
comment
@Etherealone По той же причине вы бы взяли ссылку на данные или функцию вместо указателя. Вопрос о том, действительно ли необходимы ссылки, — это другой вопрос... но первоначальное обоснование ссылок, по крайней мере, согласно CFront 1989 года, — это (перефразируя) сочетание семантики значений с эффективностью указателя. Вопрос ОП интересен: существуют указатели на данные, объекты, функции и члены. Ссылки на данные, объекты и функции существуют, но ссылки на члены не существуют, и чисто из любопытства я задаюсь вопросом, почему (плюс C++ очень явно опускает это).   -  person Jason C    schedule 22.02.2014
comment
(Другими словами: вопрос не в том, зачем вам это нужно? или есть ли альтернатива?, вопрос в том, почему они явно отсутствуют? Это академический вопрос, а не обязательно практический.)   -  person Jason C    schedule 22.02.2014
comment
Одно замечание о значениях функций или даже о них для полноты картины: это неправильно, значений функций нет. Выражение, которое синтаксически обозначает тип функции как значение, в любом контексте, который не является определением той же самой функции (если только я не упускаю что-то необычное), распадается на указатель на функцию этого типа. В вашем примере void call_function_value (void function()) {... на самом деле означает: void call_function_value (void (*function)()) {... Другими словами, фактический тип call_function_value равен void (void(*)())   -  person R.G.    schedule 21.02.2018


Ответы (1)


В стандарте (например, N3337 - не последний, но подходит для этого) в конце раздела 8.3.3.3 есть примечание, которое гласит:

[Примечание: см. также 5.3 и 5.5. Тип «указатель на член» отличается от типа «указатель», то есть указатель на член объявляется только с помощью синтаксиса декларатора указателя на член, а не синтаксиса декларатора указателя. В C++ нет типа «ссылка на член». — примечание в конце]

Кроме того, конечно, нет операторов типа "ссылка на член" (гипотетически лучшее, что я могу придумать, это что-то вроде ->& и .&, хотя они не согласуются с разыменованием ссылок на данные и функции, которые требуют без специального оператора).

Почему?

Что касается того, почему; после небольшого забавного исторического расследования и не найдя никаких существующих заметок по нему (я вернулся к Cfront 2.0, где указатель -to-member впервые был поддержан -- редактировать: в соответствии с a гораздо более достоверный документ, на самом деле эта функция впервые была поддержана в Cfront 1.2), я спросил самого человека, и вот ответ:

Date: Sat, 22 Feb 2014 10:12:51 -0500
From: Bjarne Stroustrup <...>
Subject: Re: On lack of reference-to-member and CFront 2.0

On 2/22/2014 6:40 AM, Jason C wrote:
> My question is: C++ very clearly disallows the concept of 
> "reference-to-member". Why is this? I have been doing a lot of 
> research, and I traced the origin of "pointer-to-member" back (I 
> think) to 1989 CFront 2.0. I read through the product reference manual 
> and other documentation hoping to find an explanation of some sort but 
> could not.

I don't really remember. It was 25+ years ago and the ARM is silent on 
this. I added pointers to members to eliminate the need for a rare 
breakage of the type system. I suspect that I didn't add references to 
members because it did not seem worth the effort: there was no use case.

Честно говоря, я ожидал чего-то гораздо более загадочного и сложного.

Итак, вот оно: в следующий раз, когда кто-нибудь спросит, почему нет ссылки на член, вы можете с уверенностью сказать: «Потому что ее нет!» (Примечание: смотрите мои бредни в комментариях; нужно еще провести некоторые исторические исследования, чтобы получить 100% достоверность.)

Лично я ни разу не нашел применения указателям на члены в своем собственном коде, но четкое обоснование их существования дано в Эволюция C++: 1985-1989, стр. 222-223.


Кстати, ваш синтаксис для вызова гипотетической функции ссылки на член:

object.method();

... не имеет особого смысла, так как синтаксически невозможно отличить это от вызова фактического члена с именем method().

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

person Jason C    schedule 22.02.2014
comment
Ваша заметка «Кстати» должна быть вашим ответом, ИМО. Отдельные операторы ->& и .& были бы бесполезны, большое преимущество ссылок заключается в том, что вам не нужно разыменовывать их, как указатели, и ваше последнее примечание прекрасно объясняет, почему это не сработает. - person ; 22.02.2014
comment
@hvd Это хороший момент; Интересно, была ли возникшая в результате синтаксическая двусмысленность первоначальным обоснованием запрета на это. На самом деле не было бы другого последовательного способа разыменования ссылки; все было бы особым случаем только для ссылок на членов. - person Jason C; 22.02.2014
comment
мои 2 ¢: ссылка - это имя для данного объекта. Имя члена не может использоваться само по себе (кроме как с неявным this->), поэтому было бы бесполезно создавать псевдоним. - person Potatoswatter; 22.02.2014
comment
@Potatoswatter: см. обновление. Похоже, потому что это бесполезно, это победитель! - person Jason C; 22.02.2014
comment
Страуструп упомянул в более позднем ответе: «На самом деле, был/есть технический меморандум Bell Labs, который может пролить свет, но у меня его нет под рукой». -- В настоящее время я нахожусь на миссии, чтобы найти и получить это и сообщу здесь, если и когда я это сделаю. Я не знаю, есть ли архив в Интернете, я скрещиваю пальцы, что у Alcatel-Lucent есть документы и они готовы поделиться. - person Jason C; 22.02.2014
comment
В этом документе говорится, что выпуски 1.1 (июнь 1986 г.) и 1.2 (февраль 1987 г.) в первую очередь выпуски исправлений ошибок, но также добавлены указатели на члены и защищенные члены (§4.1). Ранее в отношении указателей на члены говорится: Основные функции версии 2.0 впервые были представлены в [Stroustrup ,1987c] и обобщены в исправленной версии этой статьи [Stroustrup,1989b]. 1987c — это Эволюция C++: 1985−1987 из конференции USENIX 1987 года. Я тоже пытаюсь найти этот документ. - person Jason C; 22.02.2014
comment
Я нашел документ 1989b в архивах USENIX, но до сих пор Я не могу найти газету 1987 года. Статья 1989 года не проливает свет на отсутствие ссылок, но утверждает, что указатели на члены более подробно описаны в Stan Lippman and Bjarne Stroustrup, Pointers to Members in C++, Proc. Конференция USENIX C++, Денвер, октябрь 1988 г. Итак, вам нужно найти два документа USENIX. Материалы конференции 1988 года трудно найти. - person Jason C; 22.02.2014
comment
Я до сих пор не смог получить упомянутые документы, но я не забыл. - person Jason C; 04.03.2014
comment
@JasonC, 2 с половиной года спустя - повезло? :) - person SergeyA; 01.06.2016
comment
IMO, поскольку круглые скобки требуются при вызове функции указателя на член, они должны требоваться при вызове гипотетической ссылки на член (object.method)(). Если ссылка на член также не является членом класса, то синтаксис object.method() будет иметь смысл, поскольку это просто псевдоним другого члена объекта. - person user666412; 03.02.2017
comment
Но я согласен, что это был бы просто (хороший) синтаксический сахар (опять же, бесполезный). - person user666412; 03.02.2017
comment
@user666412 user666412 Добавление круглых скобок ничего не сделает для устранения фатальной двусмысленности такого выражения. (object.member_function)(args) эквивалентно object.member_function(args). Итак, мы так и не ушли с Квадрата 1. - person underscore_d; 17.11.2018