Як призначити псевдонім імені функції в C ++?


100

Створити нове ім’я для типу, змінної чи простору імен легко. Але як призначити нове ім’я функції? Наприклад, я хочу використовувати ім'я hollerдля printf. #define очевидно ... будь-який інший спосіб?

Рішення:

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

Дякую всім вам. Моїм колегам сподобається це, побачивши void (&NewName)(some_vector&, float, float, float, float) = OldName;під час моєї наступної реєстрації.
Агнел Куріан

19
не настільки, наскільки вони люблять бачити, як ви використовуєте випадкові імена для стандартних функцій бібліотеки.
jalf

2
Я тут не псуюся printf. Це був лише приклад. Проблема тут пов'язана з обмеженнями англійської мови, ніж будь-що інше. У мене є одна функція, яка служить цілі A і B, але я просто не можу знайти одне ім’я, яке служить обом цілям.
Агнел Куріан

2
@Neil, точно. T &a = b;створює нову назву для b. typedefдля типів та namespace A=B;просторів імен.
Агнел Куріан

2
Є using BaseClass::BaseClassMethod, і є using AliasType = Type;, і є навіть namespace AliasNamespace = Namespace;. Чого нам не вистачаєusing AliasFunction = Function;
anton_rh

Відповіді:


114

Існують різні підходи:

  • З C ++ 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);
    

1
Відмінно, як щодо C ++ 98? У мене є перевантаження перевантажень класу w / 2, які використовуються для встановлення та скидання. Всередині немає проблем. Для зовнішніх користувачів я хотів псевдонім як "встановити", щоб він був інтуїтивно зрозумілим для контексту (встановити за замовчуванням, clear () 'd тощо; скинути робочий об'єкт). Методи класу: (1) "void (& set) (const string &, const bool, const bool);" (2) void (& set) (const string &, const int, const bool); 2 "скидання" w / відповідні підписи виконують роботу. Оскільки я маю підпис у декларації класу, чи можу я просто ініціалізувати клас:: встановити (скинути), встановити (скинути). Якщо ні, чи працюватиме ваш явний static_cast приклад?
Luv2code

8
Здається, існує проблема із constexprпідходом змінних шаблонів: псевдонім не може робити виведення типу. Компілятор вимагає від мене вказати список параметрів шаблону (я пишу функцію варіативного шаблону): не можу посилатися на змінний шаблон `alias_to_old 'без списку аргументів шаблону
user69818

1
constexpr auto new_fn_name = old_fn_nameпрацює в C ++ 11 (принаймні, в gcc 4.9.2) і краще, ніж розміщення &. Він не вимагає, щоб виклик виконувався завжди через покажчик, і, таким чином, дозволяє вбудовувати функцію замість виклику.
они

З лямбдами C ++ 14, я зміг зробити наступне, що також повинно працювати, коли цільова функція має багаторазові перевантаження: constexpr auto holler = [] ( auto &&...args ) { return printf( std::forward<decltype(args)>( args )... ); };
Ентоні Холл

1
Використання std::mem_fnце НЕ псевдонім , так як він виконує набагато більше магії за змістом.
cgsdfc

35

Ви можете створити покажчик функції або посилання на функцію:

void fn()
{
}

//...

void (*p)() = fn;//function pointer
void (&r)() = fn;//function reference

2
Це займає торт. Я не знав про функціональні посилання.
Агнель Куріан

@Vulcan: Вони майже однакові в тому, що ви можете називати їх обох однаковим синтаксисом, але їхні адреси трохи відрізняються. r не займає власний простір пам'яті з адресою.
Брайан Р. Бонді

1
Як би ви зателефонували fn, використовуючи псевдонім? Чи можете ви пояснити покажчик функції та посилання на функцію? Чим вони відрізняються? Вони тут однакові?
ma11hew28

1
@Matt, Ви називаєте це саме так, як ви б назвали fn. r();
Агнел Куріан

як би ти це зробив для методу екземпляра? EDIT: це, здається, складено:void (&r)() = this->fn;
Сем

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

Ви повинні штрафувати.


Чи не printf у глобальному просторі імен?
Агнел Куріан

3
це глобально, якщо ви включили <stdio.h>, але в std, якщо ви включили <cstdio>
Injektilo

@einpoklum: У decltype немає нічого поганого , але відповідь - з 2010 року. Тоді цього не було, decltypeяк це було введено в c ++ 11. Крім того, це також повинно працювати з доброю старою звичайною С.
Фіделукс


7

Використовуйте вбудовану обгортку. Ви отримуєте обидва API, але зберігайте єдину реалізацію.


3

Від 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)...); \
}

1

З загальними лямбдами C ++ 14 я зміг зробити наступне, що також повинно працювати, коли цільова функція має кілька перевантажень:

constexpr auto holler = [] ( auto &&...args ) {
        return printf( std::forward<decltype(args)>( args )... );
    };

Га! Мені це сумно, навіть @ user5534993, який спочатку підштовхнув вас до подання цієї відповіді, не зміг підняти свою заяву. Ну ось, майте на собі.
FeRD

0

Варто згадати тут IMO, що хоча оригінальне запитання (і чудові відповіді), безумовно, корисне, якщо ви хочете перейменувати функцію (для цього є вагомі причини!), Якщо все, що ви хочете зробити, - це викреслити глибокий простір імен, але збережіть ім'я, для цього є 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, інакше ви забруднити глобальний простір імен.

Це не те саме, що "перейменування" вище, але часто є те, що дійсно хочеться.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.