вывод шаблона: почему определения шаблонов указателей на функции не совпадают, когда они являются константными и / или ссылочными? - PullRequest
0 голосов
/ 29 января 2020

После вопроса Возможно ли исправить указатели на функции-члены iostream cout / cerr, выводимые как 1 или true? Я пытаюсь написать совместимый с C ++ 98 способ печати любого указателя на функцию.

Для этого я использую поддельный шаблон C ++ "variadi c", т. Е. Пишу все определения функций вплоть до n параметров. Однако мой фиктивный вариант c работает только для указателя на функцию с 0 аргументами, как показано в следующем примере: https://godbolt.org/z/x4TVHS

#include<iostream>

template<typename Return>
std::ostream& operator <<(std::ostream& os, Return(*pointer)() ) {
    return os << (void*) pointer;
}

template<typename Return, typename T0>
std::ostream& operator <<(std::ostream& os, Return(*pointer)( const T0& t0 ) ) {
    return os << (void*) pointer;
}

template<typename Return, typename T0, typename T1>
std::ostream& operator <<(std::ostream& os, Return(*pointer)( const T0& t0, const T1& t1 ) ) {
    return os << (void*) pointer;
}

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

int main() {
    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:
//    1. funptr 0x100401080
//    2. funptr 1
//    3. funptr 1

Если я напишу эквивалентную версию, используя C ++ 11 реальных шаблонов c, тогда все работает нормально: https://godbolt.org/z/s6wdgp

#include<iostream>

template<typename Return, typename... Args>
std::ostream& operator <<(std::ostream& os, Return(*pointer)( Args... ) ) {
    return os << (void*) pointer;
}

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

int main() {
    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:
//    1. funptr 0x100401080
//    2. funptr 0x100401087
//    3. funptr 0x100401093

После анализа кода я заметил, что единственное различие между ними состоит в том, что типы на C ++ 11 пример не является константными ссылками. Затем я удалил константу и ссылку из C ++ 98, и она начала работать: https://godbolt.org/z/ZrF66b

#include<iostream>

template<typename Return>
std::ostream& operator <<(std::ostream& os, Return(*pointer)() ) {
    return os << (void*) pointer;
}

template<typename Return, typename T0>
std::ostream& operator <<(std::ostream& os, Return(*pointer)( T0 ) ) {
    return os << (void*) pointer;
}

template<typename Return, typename T0, typename T1>
std::ostream& operator <<(std::ostream& os, Return(*pointer)( T0, T1 ) ) {
    return os << (void*) pointer;
}

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

int main() {
    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:
//    1. funptr 0x100401080
//    2. funptr 0x100401087
//    3. funptr 0x100401093

Почему определения шаблонов указателей на функции не совпадают, когда они const и / или ссылки?

1 Ответ

1 голос
/ 01 февраля 2020
template<typename Return, typename T0>
std::ostream& operator <<(std::ostream& os, Return(*pointer)( const T0& t0 ) ) {
  return os << (void*) pointer;
}
void f(int) {}
os << f;

это не работает, потому что нет типов Return и T0, таких, что Return(*)(const T&) соответствует void(*)(int) точно .

В целом, переопределение операторов не в связанном пространстве имен - это плохо, потому что это очень fr agile. И вам не разрешено переопределять операторы в std. Так что этот план плохой.

...