Как назначить псевдоним имени функции в C++?

Легко создать новое имя для типа, переменной или пространства имен. Но как мне присвоить новое имя функции? Например, я хочу использовать имя holler для printf. #define очевидно... а как иначе?

Решения:

  1. #define holler printf
  2. void (*p)() = fn; //function pointer
  3. void (&r)() = fn; //function reference
  4. inline void g(){ f(); }

person Agnel Kurian    schedule 16.06.2010    source источник
comment
Всем спасибо. Моим коллегам понравится видеть void (&NewName)(some_vector&, float, float, float, float) = OldName; в моем следующем заезде.   -  person Agnel Kurian    schedule 16.06.2010
comment
не так сильно, как им понравится видеть, что вы используете случайные имена для стандартных библиотечных функций.   -  person jalf    schedule 16.06.2010
comment
Я не связываюсь с printf здесь. Это был только пример. Проблема здесь больше связана с ограничениями английского языка, чем с чем-либо еще. У меня есть одна функция, служащая цели А и цели Б, но я просто не могу найти здесь одно имя, служащее обеим целям.   -  person Agnel Kurian    schedule 16.06.2010
comment
Как именно вы создаете псевдоним для переменной? Если под этим вы не подразумеваете ссылку или указатель.   -  person    schedule 16.06.2010
comment
@Нил, точно. T &a = b; создает новое имя для b. typedef для типов и namespace A=B; для пространств имен.   -  person Agnel Kurian    schedule 17.06.2010
comment
Один неочевидный момент со ссылками на функции и указателями: если вы определяете их в заголовке, они должны быть объявлены как static или extern (в последнем случае с соответствующим определением в исходном файле) - иначе вы можете получить множественную ошибку определения. Упомянул об этом только потому, что мы обсуждаем псевдонимы (их обычно можно использовать в заголовках) в этом вопросе.   -  person avtomaton    schedule 21.11.2015
comment
Есть using BaseClass::BaseClassMethod, и есть using AliasType = Type;, и есть даже namespace AliasNamespace = Namespace;. Чего нам не хватает, так это using AliasFunction = Function;   -  person anton_rh    schedule 01.03.2018


Ответы (7)


Существуют разные подходы:

  • С С++ 11 с неперегруженными функциями без шаблонов вы можете просто использовать:

    const auto& new_fn_name = old_fn_name;
    
  • Если эта функция имеет несколько перегрузок, вы должны использовать static_cast:

    const auto& new_fn_name = static_cast<OVERLOADED_FN_TYPE>(old_fn_name);
    

    Пример: есть две перегрузки функции std::stoi

    int stoi (const string&, size_t*, int);
    int stoi (const wstring&, size_t*, int);
    

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

    const auto& new_fn_name = static_cast<int(*)(const string&, size_t*, int)>(std::stoi);
    

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

  • С C++14 вы можете пойти еще дальше с constexpr переменными шаблона. Это позволяет вам создавать псевдонимы шаблонных функций:

    template<typename T>
    constexpr void old_function(/* args */);
    
    template<typename T>
    constexpr auto alias_to_old = old_function<T>;
    
  • Кроме того, начиная с C++11, у вас есть функция с именем std::mem_fn, которая позволяет создавать псевдонимы для функций-членов. См. следующий пример:

    struct A {
       void f(int i) {
          std::cout << "Argument: " << i << '\n';
       }
    };
    
    
    A a;
    
    auto greet = std::mem_fn(&A::f); // alias to member function
    // prints "Argument: 5"
    greet(a, 5); // you should provide an object each time you use this alias
    
    // if you want to bind an object permanently use `std::bind`
    greet_a = std::bind(greet, a, std::placeholders::_1);
    greet_a(3); // equivalent to greet(a, 3) => a.f(3);
    
person sasha.sochka    schedule 20.09.2013
comment
Отлично, а как насчет C++98? У меня есть класс с двумя перегрузками сброса, используемыми для установки и сброса. Внутри без проблем. Для внешних пользователей я хотел установить псевдоним, чтобы он был интуитивно понятен для контекста (установите конструкцию по умолчанию, очистку () и т. Д., Сбросьте рабочий объект). Методы класса: (1) void (&set)(const string&,const bool,const bool); (2) void (&set)(const string&,const int,const bool); 2 сброса с соответствующими подписями делают свою работу. Поскольку у меня есть подпись в объявлении класса, могу ли я просто инициализировать класс, :set(reset),set(reset). Если нет, будет ли работать ваш явный пример static_cast? - person Luv2code; 04.07.2015
comment
Кажется, есть проблема с подходом constexpr переменных шаблона: псевдоним не может делать вывод типа. Компилятор требует, чтобы я предоставил список параметров шаблона (я пишу функцию шаблона с переменным числом аргументов): нельзя ссылаться на шаблон переменной `alias_to_old' без списка аргументов шаблона. - person user69818; 11.02.2016
comment
constexpr auto new_fn_name = old_fn_name работает в C++11 (по крайней мере, в gcc 4.9.2) и лучше, чем размещение &. Это не требует, чтобы вызов всегда выполнялся через указатель, и, таким образом, позволяет встраивать функцию вместо вызова. - person ony; 10.03.2016
comment
С помощью общих лямбда-выражений C++14 я смог сделать следующее, что также должно работать, когда целевая функция имеет несколько перегрузок: constexpr auto holler = [] ( auto &&...args ) { return printf( std::forward<decltype(args)>( args )... ); }; - person Anthony Hall; 07.12.2016
comment
Использование std::mem_fn не является псевдонимом, поскольку оно выполняет гораздо больше магии за смыслом. - person cgsdfc; 21.02.2019
comment
@AnthonyHall, ваш комментарий работал у меня, используя перегруженные функции шаблонов с переменным числом аргументов в C++17. ИМХО, это заслуживает правильного ответа, чтобы привлечь больше внимания и отдельного обсуждения. - person user5534993; 28.04.2020

Вы можете создать указатель на функцию или ссылку на функцию:

void fn()
{
}

//...

void (*p)() = fn;//function pointer
void (&r)() = fn;//function reference
person Brian R. Bondy    schedule 16.06.2010
comment
Это занимает торт. Я не знал о ссылках на функции. - person Agnel Kurian; 16.06.2010
comment
@Vulcan: они почти одинаковы в том смысле, что вы можете вызывать их обоих с одинаковым синтаксисом, но их адреса немного отличаются. r не занимает собственного пространства памяти, содержащего адрес. - person Brian R. Bondy; 16.06.2010
comment
Как бы вы назвали fn, используя псевдоним? Можете ли вы объяснить указатель функции и ссылку на функцию? Насколько они разные? Они здесь одинаковые? - person ma11hew28; 10.08.2011
comment
@Matt, ты называешь это точно так же, как назвал бы fn. r(); - person Agnel Kurian; 28.06.2012
comment
как бы вы сделали это для метода экземпляра? РЕДАКТИРОВАТЬ: кажется, что это компилируется: void (&r)() = this->fn; - person Sam; 18.09.2013
comment
@ Сэм, я только что написал свой ответ, посмотри на него. - person sasha.sochka; 20.09.2013
comment
@BrianR.Bondy Р. Бонди Предположим, я вызываю функцию класса B из класса A, например: objectB.foo(); как я могу дать ссылку на функцию foo() в классе A?? - person Deepak; 18.12.2014

typedef int (*printf_alias)(const char*, ...);
printf_alias holler = std::printf;

Должно быть хорошо.

person jer    schedule 16.06.2010
comment
Разве printf не находится в глобальном пространстве имен? - person Agnel Kurian; 19.03.2013
comment
он глобальный, если вы включили ‹stdio.h›, но в std, если вы включили ‹cstdio› - person Injektilo; 11.04.2013
comment
@einpoklum: нет ничего плохого в decltype, но ответ относится к 2010 году. , Тогда не было decltype, как это было введено в С++ 11. Кроме того, это также должно работать со старым добрым простым C. - person Phidelux; 05.01.2019

Используйте встроенную оболочку. Вы получаете оба API, но сохраняете единую реализацию.

person John    schedule 16.06.2010

Из fluentcpp: ALIAS_TEMPLATE_FUNCTION(f, g)

#define ALIAS_TEMPLATE_FUNCTION(highLevelF, lowLevelF) \
template<typename... Args> \
inline auto highLevelF(Args&&... args) -> decltype(lowLevelF(std::forward<Args>(args)...)) \
{ \
    return lowLevelF(std::forward<Args>(args)...); \
}
person sailfish009    schedule 04.01.2018

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

constexpr auto holler = [] ( auto &&...args ) {
        return printf( std::forward<decltype(args)>( args )... );
    };
person Anthony Hall    schedule 25.07.2020
comment
Ха! Это меня огорчает, даже @user5534993, который первоначально подтолкнул вас к отправке этого ответа, а затем не смог проголосовать за вас. Ну, вот, возьми один на меня. - person FeRD; 21.08.2020

Здесь стоит упомянуть ИМО, что, хотя исходный вопрос (и отличные ответы), безусловно, полезен, если вы хотите переименовать функцию (для этого есть веские причины!), Если все, что вы хотите сделать, это удалить глубокое пространство имен, но сохраните имя, для этого есть ключевое слово using:

namespace deep {
  namespace naming {
    namespace convention {
      void myFunction(int a, char b) {}
    }
  }
}
int main(void){
  // A pain to write it all out every time
  deep::naming::convention::myFunction(5, 'c');

  // Using keyword can be done this way
  using deep::naming::convention::myFunction;
  myFunction(5, 'c');  // Same as above
}

Это также имеет то преимущество, что оно ограничено областью действия, хотя вы всегда можете использовать его на верхнем уровне файла. Я часто использую это для cout и endl, поэтому мне не нужно вводить ВСЕ std с классическим using namespace std; в начале файла, но это также полезно, если вы часто используете что-то вроде std::this_thread::sleep_for() в одном файле или функции, но не везде и не любые другие функции из пространства имен. Как всегда, не рекомендуется использовать его в файлах .h, иначе вы загрязните глобальное пространство имен.

Это не то же самое, что «переименование» выше, но часто это то, что действительно нужно.

person Kevin Anderson    schedule 04.01.2019