C++, эквивалентность между указателями на функции и указателями на функции-члены?

StackOverflow https://stackoverflow.com/questions/411114

  •  03-07-2019
  •  | 
  •  

Вопрос

Я привык думать о функциях-членах как о частном случае обычных функций, где функции-члены имеют дополнительный параметр в начале списка параметров для указателя «this», то есть объекта, на котором находится функция-член. должен действовать.Раньше я использовал boost::function таким образом и никогда не сталкивался с какими-либо проблемами:

boost::function f<(void)(MyObject*, int, int)> = &MyObject::method_that_takes_two_ints;

Но я видел такой синтаксис для указателей на функции-члены:

void (MyObject::*f)( int, int ) = &MyObject::method_that_takes_two_ints;

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

Что стандарт предписывает размещение параметра this?Возможно, только в моем компиляторе дополнительный аргумент this идет первым, а может быть, в других компиляторах он может быть в конце?Мне просто повезло, что мой образ мышления соответствует тому, как с этим справляются мои компиляторы (GCC4, VS2005)?Всегда ли функции-указатели-члены являются частным случаем функций-указателей с дополнительным параметром или компилятор может реализовать их по-другому?

Это было полезно?

Решение

Стандарт почти ничего не говорит о том, где this должен быть размещен указатель, и на самом деле довольно часто для функций-членов используется другое соглашение о вызовах.(Таким образом, указатель «this» — это не просто дополнительный первый аргумент, он фактически хранится в другом месте, чем обычно первый аргумент)

В частности, MSVC использует thiscall соглашение о вызовах для функций-членов и stdcall в другом месте.http://www.hackcraft.net/cpp/MSCallingConventions/#thiscall описывает различия между ними, но обратите внимание, что thiscall хранит this указатель в ECX зарегистрируйтесь, пока stdcall магазины все параметры в стеке.

Вам определенно лучше относиться к ним как к совершенно отдельным типам.Указатель на функцию-член — это нет просто указатель на функцию с дополнительным параметром.

Другие советы

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

void (MyObject::*f)( int, int ) = &MyObject::method_that_takes_two_ints;

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

MyObject o; (o.*f)(1, 2);

Указатель на функцию-член — это просто указатель на член, тип которого (на который он указывает) является типом функции.В стандарте говорится, что указатели на функции-члены не имеют собственного «типа функции-члена», на который они указывают, и который каким-то образом включал бы тип указателя this.

int main() {
    typedef void fun() const;
    fun MyObject::*mem_function_ptr = 
        &MyObject::const_method_that_takes_two_ints;
}

fun в этом коде — тип функции.Тип, который имеет «обычная» функция.Указатель на функцию, в отличие от указателя на функцию-член, является просто указателем на функцию, имеющую этот тип:

void foo() { cout << "hello"; }
int main() {
    typedef void fun();
    fun * f = &foo;
}

В то время как указатель на функцию-член имеет дополнительный уровень указателя-члена поверх этого типа функции.

Что-то о this указатель и его отношение к объекту, на который он указывает (не технические, просто теоретические вещи):

Каждая функция-член имеет скрытый параметр, называемый implicit object parameter который имеет тип MyObject& или MyObject const& в зависимости от того, есть ли у вас константная или неконстантная функция-член.Объект, для которого вы вызываете функцию-член, o, это implied object argument, который передается в параметр.В теории стандарта, составляющего правила, описывающие вызов функций-членов, неявный параметр объекта является первым скрытым параметром.Это концептуально и не означает, что это реальный случай реализации.Аргумент подразумеваемого объекта затем привязывается к этому неявному параметру объекта, что может вызвать неявные преобразования (поэтому, если вы вызываете константную функцию-член для неконстантного объекта, квалификационное преобразование преобразуется из MyObject к MyObject const&.Именно это делает неконстантные функции лучшим выбором для вызова неконстантных объектов, чем константные функции).Например, в этом коде можно сказать:

struct A {
    operator int() const { return 0; }
};

int main() { 
    A a;
    int i = a; // implicit conversion using the conversion function
}

Что подразумеваемый аргумент объекта a типа A привязан к неявному параметру объекта типа A const&, на объект которого затем указывает this указатель, имеющий тип A const* здесь.Важно отметить, что неявный параметр объекта является лишь теоретической конструкцией, предназначенной для формализации того, как составляются правила вызова функции-члена (а конструкторы их не включают), тогда как указатель this фактически существует. this является указателем, потому что, когда this был представлен, на C++ еще не было ссылок.

Надеюсь, это поможет вам понять суть дела.

Отличная статья об указателях на функции-члены — CodeProject. Указатели на функции-члены и самые быстрые делегаты C++.В этой статье описываются указатели на функции-члены, начиная с простых случаев и заканчивая указателями на виртуальные функции-члены с множественным наследованием.В качестве бонуса он предоставляет реализацию делегатов, которая может быть действительно полезной.

Да, указатели на функции и указатели на члены — совершенно разные звери.Указатели на члены должны иметь экземпляр объекта, который будет разыменован с помощью метода ->* или .* операторы.Здесь нет this параметр, используемый при создании указателя на член, потому что this определяется при использовании указателя на член (объект слева от ->* или .*).

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

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

Обратите внимание, что размер указателя на функцию-член может отличаться при использовании разных компиляторов.

Еще следует отметить, как написано в Блог «Старая новая вещь»:

Размер функции указателя к члену может измениться в зависимости от класса.

Это определенно разные типы, и любые ваши предположения будут зависеть от платформы/компилятора.

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

Чтобы ответить на все вопросы:Да, это специальные указатели, отличные от обычных указателей.Да, boost::function распознает их.

Стандарт ничего не говорит о внутренних деталях стека вызовов.Фактически, многие компиляторы могут использовать целочисленные регистры, регистры с плавающей запятой и/или стек в зависимости от фактического списка аргументов.Указатель this — это еще один особый случай.

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

Чтобы дополнить ответ всех остальных, Boost.Function работает, специализируя оператор присваивания на указателях на функции-члены, чтобы позволить ему определять, когда вы его передали.Когда вы вызываете эту функцию, она внутренне интерпретирует ее в правильный метод вызова указателя функции-члена ((obj->*fun)(args)).

Думаю, вам будет интересна эта ссылка:

http://www.parashift.com/c++-faq-lite/pointers-to-members.html

Это очень хорошее описание всего, что вы хотели бы знать об указателях на члены.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top