如何在C++中为函数名称分配别名?

130

创建一个新类型、变量或命名空间的名称很容易,但如何给函数分配新名称呢?例如,我想用名称 holler 代替 printf。使用 #define 很显然...还有其他方法吗?

解决方案:

  1. #define holler printf
  2. void (*p)() = fn; // 函数指针
  3. void (&r)() = fn; // 函数引用
  4. inline void g(){ f(); }

2
谢谢大家。我的同事们会很高兴看到在我的下一个提交中出现 void (&NewName)(some_vector&, float, float, float, float) = OldName; - Agnel Kurian
23
他们不会像看到你使用标准库函数的随机名称那样喜欢你,但也不至于讨厌。 - jalf
4
我这里并没有玩弄printf。那只是一个例子。这里的问题更多地与英语的限制有关而不是其他什么。我有一个函数,既可以用于目的A,也可以用于目的B,但我无法找到一个名称同时适用于两种情况。 - Agnel Kurian
2
@Neil,确切地说。T &a = b;b创建了一个新名称。类型使用typedef,命名空间使用namespace A=B; - Agnel Kurian
4
using BaseClass::BaseClassMethod,也有 using AliasType = Type,甚至有 namespace AliasNamespace = Namespace。我们所缺少的是 using AliasFunction = Function - anton_rh
显示剩余3条评论
8个回答

147

有不同的方法:

  • 使用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的函数来为成员函数创建别名。请看下面的例子:<\p><\li>
    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怎么样?我有一个类,其中有两个“reset”重载用于设置和重置。在内部没有问题。对于外部用户,我想将其别名为“set”,以便在上下文中直观(设置默认构造、清除等;重置工作对象)。类方法:(1) "void (&set)(const string&,const bool,const bool);" (2) void (&set)(const string&,const int,const bool); 2个具有相应签名的“reset”执行工作。由于我在类声明中有签名,所以我可以只是类初始化,:set(reset),set(reset)。如果不行,你的显式static_cast示例会起作用吗? - Luv2code
    11
    使用constexpr模板变量的方法似乎存在问题:别名无法进行类型推导。编译器要求我提供模板参数列表(我正在编写一个可变参数模板函数):不能在没有模板参数列表的情况下引用变量模板“alias_to_old” - user69818
    1
    constexpr auto new_fn_name = old_fn_name 在 C++11 中可行(至少在 gcc 4.9.2 中),比使用 & 更好。它不需要始终通过指针调用函数,因此允许将函数内联到调用的位置。 - ony
    使用C++14的通用lambda表达式,我能够做到以下操作,当目标函数有多个重载时也应该适用:`constexpr auto holler = [] ( auto &&...args ) { return printf( std::forward( args )... ); };` - Anthony Hall
    1
    使用std::mem_fn并不是一个别名,因为它在幕后执行了更多的魔法。 - cgsdfc
    @AnthonyHall,您的评论对我有用,我使用了C++17的重载可变参数模板函数。在我看来,它值得被放在一个适当的答案中以获得更多关注和单独的讨论。 - user5534993

    36
    你可以创建一个函数指针或函数引用:

    您可以创建函数指针或函数引用:

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

    4
    这真是太棒了。我之前不知道函数引用。 - Agnel Kurian
    1
    你会如何使用别名来调用 fn 函数?你能解释一下函数指针和函数引用吗?它们有什么不同?在这里它们是相同的吗? - ma11hew28
    1
    @Matt,您可以像调用fn一样调用它。r(); - Agnel Kurian
    你如何为实例方法执行此操作?编辑:这似乎可以编译:void (&r)() = this->fn; - Sam
    @Sam,我刚写完我的答案了,请看一下。 - sasha.sochka
    显示剩余2条评论

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

    应该可以满足你的需求。


    printf不是在全局命名空间中吗? - Agnel Kurian
    3
    如果您包含了<stdio.h>,那么它是全局的,但如果您包含<cstdio>,则是在std中。 - Injektilo
    @einpoklum:decltype 没有任何问题,但是这个答案是从2010年的。当时还没有 decltype,因为它是在c++11中引入的。此外,这也适用于好老的纯C。 - Phidelux

    10

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


    7
    使用内联包装器。您将获得两个API,但保持单个实现。

    7
    使用C++14的通用lambda表达式,我可以进行以下操作,当目标函数有多个重载时,此方法应该也适用:
    constexpr auto holler = [] ( auto &&...args ) {
            return printf( std::forward<decltype(args)>( args )... );
        };
    

    哈!这让我感到难过,即使是最初推动你提交这个答案的@user5534993也不能给你一个赞。好吧,在这里,我送你一个赞。 - FeRD

    6

    From 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)...); \
    }
    

    4

    这里值得一提的是,如果你只想去除一个深层命名空间但保留名称,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
    }
    

    这样做的好处是它被限定在一个范围内,尽管你总是可以在文件的顶层使用它。我经常用它来代替在文件顶部使用经典的using namespace std;,这样就不需要引入所有的std,比如对于coutendl。如果你在一个文件或函数中经常使用std::this_thread::sleep_for(),但不是在每个地方使用,也不使用命名空间中的其他函数,那么它也很有用。一如既往,不建议在头文件中使用它,否则会污染全局命名空间。
    这与上面的“重命名”不同,但通常是真正想要的。

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接