Когда вы делаете E1.E2
, вы не говорите об общем свойстве того типа вещи, которым является E1
. Вы просите получить доступ к объекту в объекте , обозначенном E1
, где имя объекта, к которому нужно получить доступ, - E2
. Если E2
статический, он обращается к классу static; если E2
не является статичным, то он получает доступ к элементу , специфичному для этого объекта . Это важно.
Переменные-члены становятся подобъектами. Если в вашем классе S
был элемент нестатических данных int i;
, s.i
является ссылкой на int
. Эта ссылка, с точки зрения int&
, ведет себя не иначе, как любая другая int&
.
Позвольте мне сказать это более четко: любой int*
или int&
может указывать / ссылаться на int
, который является законченным объектом, или int
, который является подобъектом какого-либо другого объекта. Таким образом, одиночная конструкция int&
может выполнять двойную функцию. *
Учитывая это понимание s.i
, каково будет предполагаемое значение s.f
? Ну, это должно быть похоже, верно? s.f
будет чем-то вроде того, что при вызове с params
будет эквивалентно выполнению s.f(params)
.
Но это не та вещь, которая существует в C ++.
В C ++ нет языковой конструкции, которая могла бы представлять значение s.f
. Такая конструкция должна хранить ссылку на s
, а также член S::f
.
Указатель функции не может этого сделать. Функциональные указатели должны иметь возможность взаимного преобразования указателей с void*
**. Но такой s.f
должен хранить элемент S::f
, а также ссылку на сам s
. Таким образом, по определению, он должен быть больше, чем void*
.
Указатель члена тоже не может этого сделать. Указатели-члены явно не несут с собой свой объект this
(в этом вся суть); Вы должны предоставить их во время вызова, используя специальный синтаксис вызова указателя члена .*
или .->
.
О, есть способы закодировать это в языке: лямбды, std::bind
и т. Д. Но не существует конструкции на уровне языка, которая бы имела такое точное значение.
Поскольку C ++ таким образом асимметричен, где s.i
имеет кодируемое значение, но не s.f
, C ++ делает некодируемое значение недопустимым.
Вы можете спросить, почему такая конструкция не просто создается. Это не так важно. Язык работает отлично, как есть, и из-за сложности того, чем должен быть s.f
, вероятно, лучше всего заставить вас использовать лямбду (для которой, по общему признанию, должны быть способы сделать ее короче, чтобы писать такие вещи) если ты этого хочешь.
И если вы хотите, чтобы обнаженная s.f
была эквивалентна S::f
(т.е. обозначает функцию-член), это тоже не работает. Во-первых, S::f
также не имеет типа; единственное, что вы можете сделать с таким значением, это преобразовать его в указатель на член. Во-вторых, указатель на функцию-член не знает, из какого объекта он пришел, поэтому, чтобы использовать его для вызова члена, вам нужно дать ему s
. Следовательно, в выражении вызова s
должно появиться дважды. Что действительно глупо.
*: есть вещи, которые вы можете делать , чтобы завершить объекты, которые вы не можете сделать с подобъектами. Но они провоцируют UB, потому что они не обнаруживаются компилятором, потому что int*
не говорит, идет он от подобъекта или нет. Что является основным моментом; никто не может отличить.
**: стандарт не требует этого, но стандарт не может сделать то, что прямо делает такую реализацию невозможной . Большинство реализаций предоставляют эту функциональность, и в основном любой загрузочный код DLL / SO полагается на него. О, и это также будет полностью несовместимо с C, что делает его не стартером.