Как назначить псевдоним имени функции в 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, но я просто не могу найти здесь одно имя, служащее обеим целям.
Agnel Kurian

2
@ Нил, именно так. T &a = b;создает новое имя для b. typedefдля типов и namespace A=B;для пространств имен.
Agnel Kurian

2
Есть using BaseClass::BaseClassMethod, и есть using AliasType = Type;, и даже есть namespace AliasNamespace = Namespace;. Нам не хватаетusing AliasFunction = Function;
anton_rh 01

Ответы:


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? У меня есть класс с 2 перегрузками "сброса", используемый для установки и сброса. Внутри нет проблем. Для внешних пользователей я хотел использовать псевдоним «set», чтобы он был интуитивно понятен для контекста (установить сконструированный по умолчанию, clear () 'd и т. Д .; сбросить рабочий объект). Методы класса: (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?
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
Это берет торт. Я не знал о ссылках на функции.
Agnel Kurian

@Vulcan: они почти одинаковы в том, что вы можете вызывать их обоих с одинаковым синтаксисом, но их адреса немного отличаются. r не занимает свое собственное пространство памяти, содержащее адрес.
Брайан Р. Бонди

1
Как бы вы позвонили fn, используя псевдоним? Можете ли вы объяснить указатель на функцию и ссылку на функцию? Насколько они разные? Они здесь такие же?
ma11hew28

1
@Matt, вы называете это именно так, как вы бы назвали фн. r();
Agnel Kurian

как бы вы сделали это для метода экземпляра? РЕДАКТИРОВАТЬ: похоже, это компилируется:void (&r)() = this->fn;
Сэм,

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

Должен ты в порядке.


Разве printf не входит в глобальное пространство имен?
Agnel Kurian

3
это глобально, если вы включили <stdio.h>, но в std, если вы включили <cstdio>
Injektilo

@einpoklum: Существует ничего плохого decltype , но ответ от 2010. Тогда не было , decltypeкак это было введено в C ++ 11. Кроме того, это также должно работать со старым добрым простым C.
Phidelux


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

Здесь стоит упомянуть ИМО, что хотя исходный вопрос (и отличные ответы) определенно полезен, если вы хотите переименовать функцию (для этого есть веские причины!), Если все, что вы хотите сделать, это вырезать глубокое пространство имен, но сохраните имя, для этого есть 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.