C++ 11:如何为函数设置别名?

And*_*zos 61 c++ linux gcc c++11

如果我在命名空间栏中有一个类Foo:

namespace bar
{
    class Foo { ... }
};
Run Code Online (Sandbox Code Playgroud)

那我可以:

using Baz = bar::Foo;
Run Code Online (Sandbox Code Playgroud)

现在就像我在名称空间中使用名称Baz定义类一样.

是否有可能为功能做同样的事情?

namespace bar
{
    void f();
}
Run Code Online (Sandbox Code Playgroud)

然后:

using g = bar::f; // error: ‘f’ in namespace ‘bar’ does not name a type
Run Code Online (Sandbox Code Playgroud)

最干净的方法是什么?

解决方案还应该适用于模板功能.

定义:如果某个实体B是A 的别名,那么与源代码中的B替换A的任何或所有用法(当然不是声明​​或定义)相比,(剥离的)生成的代码保持不变.例如typedef A B是别名. #define B A是一个别名(至少). T& B = A不是别名,B可以有​​效地实现为间接指针,一个"unaliased"A可以使用"立即语义".

Jer*_*ock 64

您可以使用完美转发定义函数别名(有一些工作):

template <typename... Args>
auto g(Args&&... args) -> decltype(f(std::forward<Args>(args)...)) {
  return f(std::forward<Args>(args)...);
}
Run Code Online (Sandbox Code Playgroud)

即使f过载和/或功能模板,此解决方案也适用.

  • 这会丢弃 noexcept 信息,因此不再是 C++17 中的精确别名:http://coliru.stacked-crooked.com/a/f3ee222a98c97184 (4认同)
  • 如果`f`是不能使用类型推导的函数模板,则不适用. (3认同)
  • @ user1131467任何使用模板参数确定其返回类型的函数(如`boost :: lexical_cast`),或使用模板参数作为*事实上*参数(如`std :: get`). (3认同)
  • @RMartin如果你没有指定它会假定"ExplicitArgs"为空它可以将它重写为`template <typename ... ExplicitArgs,typename ... Args> void foo(Args ... args)`并调用另一个像`f <ExplicitArgs ...>(args ...)`.不幸的是,它只对尾随模板参数包进行"假定为空",而不是对于前一个尾随的那个. (3认同)
  • @RMartin实际上GCC和Clang已经做到了这一点,所以上面的ExplicitArgs方式甚至可以在野外运行!他们认为两个模板参数包都"尾随". (2认同)
  • 我该如何使用这个? (2认同)

Ker*_* SB 20

类型的,所以它们可以与别名typedefusing(在C++ 11).

函数更像是对象,因此没有别名的机制.充其量你可以使用函数指针或函数引用:

void (*g)() = &bar::f;
void (&h)() = bar::f;

g();
h();
Run Code Online (Sandbox Code Playgroud)

同样,没有别名变量的机制(通过指针或引用).

  • @KerrekSB我说如果你有`void f(); void(&rf)()= f;`然后`&f`和`&rf`都会产生`f`的地址.但是如果`rf`是一个函数指针,`void(*rf)()= f;`,则`&rf`返回它的地址而不是函数的地址,这是坏的. (3认同)
  • Bashphorism 2警报?!最后一个定义了一个全新的功能. (2认同)

Paw*_*ica 17

constexpr函数指针可以用作函数别名.

namespace bar
{
    int f();
}

constexpr auto g = bar::f;
Run Code Online (Sandbox Code Playgroud)

在使用别名的位置,即使在没有任何优化的情况下进行编译,编译器也会调用别名函数.

使用GCC7以下用法

int main()
{
    return g();
}
Run Code Online (Sandbox Code Playgroud)

main:
  push rbp
  mov rbp, rsp
  call bar::f()  # bar::f() called directly.
  pop rbp
  ret
Run Code Online (Sandbox Code Playgroud)

程序集在Compiler Explorer中生成.

  • @jaskmar,我更新了答案,假设您的意思是间接/直接调用而不是内联。内联与此处无关。 (2认同)

Mar*_*ork 15

绝对:

#include <iostream>

namespace Bar
{
   void test()
   {
      std::cout << "Test\n";
   }


   template<typename T>
   void test2(T const& a)
   {
      std::cout << "Test: " << a << std::endl;
   }
}

void (&alias)()        = Bar::test;
void (&a2)(int const&) = Bar::test2<int>;

int main()
{
    Bar::test();
    alias();
    a2(3);
}
Run Code Online (Sandbox Code Playgroud)

尝试:

> g++ a.cpp
> ./a.out
Test
Test
Test: 3
>
Run Code Online (Sandbox Code Playgroud)

引用是现有对象的别名.
我刚刚创建了一个函数的引用.引用可以与原始对象完全相同的方式使用.

  • 对此更好的习语是:`constexpr auto&alias = Bar :: test;`.`constexpr`部分保证在编译时替换引用.但是,`auto`部分不适用于模板化功能. (9认同)
  • “引用是现有对象的别名。我刚刚创建了对函数的引用。” ——函数不是对象。因此,函数引用*不是*现有对象的别名(就像函数指针不是对象指针一样):-) (2认同)

Jas*_*lam 11

可以将函数引入不同的范围而不更改其名称.因此,您可以使用不同的限定名称对函数进行别名:

namespace bar {
  void f();
}

namespace baz {
  using bar::f;
}

void foo() {
  baz::f();
}
Run Code Online (Sandbox Code Playgroud)


Tom*_*Tom 7

它不是标准的C++,但大多数编译器提供了这样做的方法.使用GCC,您可以这样做:

void f () __attribute__ ((weak, alias ("__f")));
Run Code Online (Sandbox Code Playgroud)

这会将符号创建f为别名__f.使用VC++,你可以这样做:

#pragma comment(linker, "/export:f=__f")
Run Code Online (Sandbox Code Playgroud)