Защо препратка към член не съществува в C++?

В 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
Вие сте прави, че препратките към методите не съществуват. Стандартът C++ не дефинира такъв тип и изрично посочва (в ненормативна бележка), когато описва типове указател към член, В C++ няма тип препратка към член. Но не мога да отговоря на въпроса ви за причината да не съществуват.   -  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 По същата причина бихте взели препратка към данни или функция вместо указател. Дали препратките са наистина необходими или не е различен дебат... но първоначалната обосновка за препратките, поне според 1989 CFront, е (перефразирано) комбиниране на семантика на стойността с ефективност на указателя. Въпросът на OP е интересен: съществуват указатели към данни, обекти, функции и членове. Съществуват препратки към данни, обекти и функции -- но препратки към членове не съществуват и, чисто от любопитство, се чудя защо (плюс това 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
Вашата между другото бележка трябва да бъде вашият отговор, IMO. Отделните оператори ->& и .& биха били безполезни, голяма полза от препратките е, че не е необходимо да ги дереферирате, както правите указатели, и последната ви бележка обяснява много добре защо това няма да работи. - person ; 22.02.2014
comment
@hvd Това е добра точка; Чудя се дали получената синтактична неяснота е била първоначалната обосновка за забраната му. Наистина няма да има друг последователен начин за дерефериране на препратката; всичко ще бъде специален случай само за препратка към членове. - person Jason C; 22.02.2014
comment
my 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 е The Evolution of C++: 1985− 1987 от конференция на USENIX през 1987 г. Опитвам се да намеря и този документ. - person Jason C; 22.02.2014
comment
Намерих статията от 1989b в архивите на USENIX, но досега Не мога да намеря документа от 1987 г. Документът от 1989 г. не хвърля светлина върху липсата на препратки, но посочва, че указателите към членове са описани по-подробно в Stan Lippman и 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 Добавянето на скоби няма да направи нищо за премахване на фаталната двусмисленост на такъв израз. (object.member_function)(args) е еквивалентно на object.member_function(args). И така, никога не сме избягали от площад 1. - person underscore_d; 17.11.2018