Можно ли исправить указатели функций-членов iostream cout/cerr, которые печатаются как 1 или true?

Если вы запустите следующее:

#include <iostream>

int main()
{
    std::cout.setf(std::ios::boolalpha);
    std::cout << &main << "\n";
    std::cout << (void*)&main << "\n";  // The workaround
    return 0;
}

// prints something like
//   true
//   0x55deee04189a

Если вы удалите std::cout.setf(std::ios::boolalpha) звоните, он просто печатает 1 вместо того true.

Если вы посмотрите на сборку https://godbolt.org/z/6CFH3P, вы заметите, чтоC++ разрешение шаблона выбирает логический оператор std::basic_ostream<char, std::char_traits<char> >::operator<<(bool).

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

Стандарт C++ определяет:

4.12 Булевы преобразования

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

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

Однако это не работает для указателей функций класса-члена: https://godbolt.org/z/zBN5Va

#include<iostream>

template<class Ret, class... Args>
std::ostream& operator <<(std::ostream& os, Ret(*p)(Args...) ) {
    return os << "funptr " << (void*)p;
}

struct test_debugger { void var() {} };
void fun_void_void(){};
void fun_void_double(double d){};
double fun_double_double(double d){return d;}

int main() {
    std::cout << "0. " << &test_debugger::var << std::endl;
    std::cout << "1. " << fun_void_void << std::endl;
    std::cout << "2. " << fun_void_double << std::endl;
    std::cout << "3. " << fun_double_double << std::endl;
}

// Prints:
//    0. 1
//    1. funptr 0x100401080
//    2. funptr 0x100401087
//    3. funptr 0x100401093

Можно ли исправить указатели функций-членов iostream cout / cerr, которые печатаются как 1 или true? Целью было бы работать с любой бесплатной функцией или функцией класса-члена без необходимости вручную преобразовывать их в(void *) указатель перед отправкой в std::cout или std::cerr.


Связанные вопросы:

  1. Печать указателя с помощью
  2. Указатель на функцию-член, всегда печатается как "1"

Обновить

Я попробовал следовать Dan M.. ( указатель на общую функцию-член в качестве параметра шаблона):

template <typename T, typename R, typename ...Args>
std::ostream& operator <<(std::ostream& os, R (T::*p)(Args...) ) {
    return os << "funptr " << (void*)p;
}

Но он выдает это предупреждение: https://godbolt.org/z/yj52hM

$ g++ -o main.exe --std=c++11 test_debugger.cpp && ./main.exe
test_debugger.cpp: In instantiation of ‘std::ostream& operator<<(std::ostream&, R (T::*)(Args ...)) [with T = test_debugger; R = int; Args = {}; std::ostream = std::basic_ostream<char>]’:
test_debugger.cpp:19:42:   required from here
test_debugger.cpp:10:31: warning: converting from ‘int (test_debugger::*)()’ to ‘void*’ [-Wpmf-conversions]
     return os << "funptr " << (void*)p;
                               ^~~~~~~~
0. funptr 0x100401860
1. funptr 0x100401080
2. funptr 0x100401087
3. funptr 0x100401093

Как правильно исправить предупреждение warning: converting from ‘int (test_debugger::*)()’ to ‘void*’ [-Wpmf-conversions]?

1 ответ

Решение

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

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

template<class C, class Ret, class... Args>
std::ostream& operator <<(std::ostream& os, Ret (C::*p)(Args...)) {
    return os << "memfunptr " << "something...";
}

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

unsigned char* internal_representation = reinterpret_cast<unsigned char*>(&p);
for(std::size_t i = 0; i < sizeof p; i++)
    os << std::hex << (int)internal_representation[i];

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

PPS Добавление перегрузок в стандартный класс, где все аргументы являются стандартными классами или фундаментальными типами, вероятно, потенциально несовместимо с будущим языковым стандартом.

PPPS Принятие адреса main технически не допускается.

Другие вопросы по тегам