Как печатать указатели функций с помощью cout?

Я хочу распечатать указатель на функцию cout и обнаружил, что он не работает. Но он работал после преобразования указателя на (void *), так что printf с% p, например

#include  using namespace std; int foo() {return 0;} int main() { int (*pf)(); pf = foo; cout << "cout << pf is " << pf << endl; cout << "cout << (void *)pf is " << (void *)pf << endl; printf("printf(\"%%p\", pf) is %p\n", pf); return 0; } 

Я скомпилировал его с g ++ и получил такие результаты:

cout << pf равно 1
cout << (void *) pf равно 0x100000b0c
printf (“% p”, pf) равен 0x100000b0c

Итак, что делает cout с типом int (*) ()? Мне сказали, что указатель функции рассматривается как bool, правда ли это? И что делает cout с типом (void *)?

Заранее спасибо.

EDIT: Во всяком случае, мы можем наблюдать содержимое указателя функции, преобразовывая его в (void *) и распечатывая его с помощью cout. Но он не работает для указателей функций-членов, и компилятор жалуется на незаконное преобразование. Я знаю, что указатели на функции-члены представляют собой сложную структуру, отличную от простых указателей, но как мы можем наблюдать содержимое указателей на функцию-член?

На самом деле существует перегрузка оператора <<, который выглядит примерно так:

 ostream & operator <<( ostream &, const void * ); 

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

Изменить: стандарт C ++ указывает:

4.12 Логические преобразования

1 Значение арифметики, enums, указателя или указателя на тип члена может быть преобразовано в rvalue типа bool.

Это единственное преобразование, указанное для указателей функций.

Что касается вашего редактирования, вы можете распечатать содержимое чего-либо, обратившись к нему с помощью указателя unsigned char . Пример указателей на функции-члены:

 #include  #include  struct foo { virtual void bar(){} }; struct foo2 { }; struct foo3 : foo2, foo { virtual void bar(){} }; int main() { void (foo3::*p)() = &foo::bar; unsigned char const * first = reinterpret_cast(&p); unsigned char const * last = reinterpret_cast(&p + 1); for (; first != last; ++first) { std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)*first << ' '; } std::cout << std::endl; } 

Вы можете думать о указателе функции как о адресе первой инструкции в машинный код этой функции. Любой указатель можно рассматривать как bool : 0 – false, а все остальное – true. Как вы заметили, при передаче в void * и задании в качестве аргумента для оператора вставки streamа ( << ), адрес печатается. (Строго просматривается, отбрасывание указателя на функцию void * не определено).

Без броска история немного сложна. Для сопоставления перегруженных функций («разрешение перегрузки») компилятор C ++ собирает набор функций-кандидатов, и из этих кандидатов выбирает «наилучшую жизнеспособную», используя при необходимости неявные преобразования. Морщина - это правила сопоставления с частичным порядком, поэтому множественные наиболее эффективные совпадения вызывают ошибку двусмысленности.

В порядке предпочтения стандартные конверсии (и, конечно, там также определенные пользователем и многоточие преобразования, а не подробные) являются

  • точное соответствие ( т. е. никакого преобразования не требуется)
  • продвижение ( например , int to float )
  • другие конверсии

Последняя категория включает в себя логические преобразования, и любой тип указателя может быть преобразован в bool : 0 (или NULL ) - false а все остальное - true . Последний отображается как 1 при передаче оператору вставки streamа.

Чтобы вместо этого получить 0 , измените вашу инициализацию на

 pf = 0; 

Помните, что инициализация указателя нулевым значением константы дает нулевой указатель.

Указатели каста на (void*) для печати их в cout – это правильная вещь (TM), чтобы делать на C ++, если вы хотите увидеть их значения.

Что касается вашего конкретного вопроса,

как мы можем наблюдать содержимое указателей на функцию-член?

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

4.12 сноска 57:

57) Правило преобразования указателей в члены (от указателя к члену базы до указателя на член производного) представляется инвертированным по сравнению с правилом для указателей на объекты (от указателя к производному от указателя к базе) (4.10, п. 10) , Эта инверсия необходима для обеспечения безопасности типа. Обратите внимание, что указатель на элемент не является указателем на объект или указателем на функцию, а правила для преобразования таких указателей не применяются к указателям на члены. В частности, указатель на член не может быть преобразован в void *.

Например, вот пример кода:

 #include  #include  #include  #include  #include  using namespace std; class Gizmo { public: void DoTheThing() { return; }; private: int foo_; }; int main() { void(Gizmo::*fn)(void) = &Gizmo::DoTheThing; Gizmo g; (g.*fn)(); // once you have the function pointer, you can call the function this way bool b = fn; // void* v = (void*)fn; // standard explicitly disallows this conversion cout << hex << fn; return 0; } 

Я отмечаю, что мой отладчик (MSVC9) может сказать мне фактический физический адрес функции-члена во время выполнения, поэтому я знаю, что должен быть какой-то способ получить этот адрес. Но я уверен, что он несоответствует, не переносится и, вероятно, включает машинный код. Если бы я пошел по этой дороге, я бы начал с адреса указателя функции (например, &fn ), отбрасывая это на пустоту * и оттуда. Это также потребует, чтобы вы знали размер указателей (разные на разных платформах).

Но я бы спросил, до тех пор, пока вы можете преобразовать указатель функции-члена в bool и оценить существование указателя, почему в реальном коде вам нужен адрес?

Предположительно, ответ на последний вопрос «так что я могу определить, указывает ли один указатель на функцию на ту же функцию, что и на другой». Справедливо. Вы можете сравнить указатели на функции для равенства:

 #include  #include  #include  #include  #include  using namespace std; class Gizmo { public: void DoTheThing() { return; }; **void DoTheOtherThing() { return; };** private: int foo_; }; int main() { void(Gizmo::*fn)(void) = &Gizmo::DoTheThing; Gizmo g; (g.*fn)(); // once you have the function pointer, you can call the function this way bool b = fn; // void* v = (void*)fn; // standard explicitly disallows this conversion cout << hex << fn; **void(Gizmo::*fnOther)(void) = &Gizmo::DoTheOtherThing; bool same = fnOther == fn; bool sameIsSame = fn == fn;** return 0; } 

В C ++ 11 можно было бы изменить это поведение, указав переменную шаблонную перегрузку operator<< (то, что это рекомендуется или нет, является другой темой):

 #include namespace function_display{ template std::ostream& operator <<(std::ostream& os, Ret(*p)(Args...) ){ // star * is optional return os << "funptr " << (void*)p; } } // example code: void fun_void_void(){}; void fun_void_double(double d){}; double fun_double_double(double d){return d;} int main(){ using namespace function_display; // ampersands & are optional std::cout << "1. " << &fun_void_void << std::endl; // prints "1. funptr 0x40cb58" std::cout << "2. " << &fun_void_double << std::endl; // prints "2. funptr 0x40cb5e" std::cout << "3. " << &fun_double_double << std::endl; // prints "3. funptr 0x40cb69" } 
  • Чтение CSV-файла в .NET?
  • Как получить список коллекции значений из app.config в WPF?
  • Вызов хранимой процедуры Oracle из C #?
  • Что на самом деле происходит в try {return x; } наконец {x = null; } заявление?
  • Объединить данные гироскопа и акселерометра
  • Направлять указатель на функцию-член в обычный указатель
  • Простой способ создания C ++ memystream из (char *, size_t) без копирования данных?
  • C # получить тип нулевого объекта
  • Как выглядит ссылка на C ++, с точки зрения памяти?
  • Как использовать Assert для проверки того, что было выбрано исключение?
  • Перечислить перечисление в C ++
  • Interesting Posts

    Данные объемной загрузки в sqlite?

    std :: fstream буферизация против ручного буферизации (почему 10-кратное усиление с ручной буферизацией)?

    Прозрачная заставка для Windows?

    Способ получения старых подкастов в iTunes

    Java8: сумма значений из определенного поля объектов в списке

    Каково значение знака вопроса в MySQL в столбце WHERE =??

    Почему setSelected on JCheckBox теряет эффект?

    В чем разница между .Equals и ==

    Несколько приложений, использующих одну и ту же базу данных входа в систему, регистрируя друг друга

    Авторизовать атрибут в ASP.NET MVC

    Как увеличить размер шрифта на графике в R?

    Как визуализировать массив объектов в React?

    Извлечение строк для первого вхождения переменной в фрейм данных

    Почему разрешение дисплея не соответствует 16: 9 или 4: 3?

    Как ядра в модуле AMD Bulldozer сравниваются с виртуальными ядрами Intel HTT и двумя отдельными ядрами с точки зрения производительности многозадачности?

    Давайте будем гением компьютера.