如何在类空间中为成员函数设置别名?

4
我希望能够从该类的对象中以多个名称调用相同的成员函数。
例如:
#include <string>
#include <stdio.h>

class Log
{
public:
    Log(std::string str)
        : log(str)
    {}

    void print() const
    {
        puts(log.c_str());
    }
    const auto& output = print;    // attempting to alias. does not work

private:
    std::string log;
};

int main()
{
    Log log("LOG: Log started.");
    log.print();
    log.output();    // both should call the same function.
    return 0;
}

这段代码在我的环境下(gcc 7.3.0)出现了错误。
main.cpp:15:15: error: non-static data member declared with placeholder ‘const auto’
         const auto& output = print;    // attempting to alias. does not work
               ^~~~
main.cpp: In function ‘int main()’:
main.cpp:25:13: error: ‘class Log’ has no member named ‘output’
         log.output();    // both should call the same function.

如何为函数名称定义别名?


5
这段代码的意思是:定义一个名为output的无返回值函数,函数类型为const。在函数体内部调用了print函数。 - Passer By
1
@路人甲,那不完全是一个别名,如果print的签名发生变化,我将不得不调整该函数。 - stimulate
1
如果您坚持要扩展@PasserBy的初始评论,以便它可以在任何print签名更改时保留:template <typename ... TT> auto output(TT&& ... tt) { return print(std::forward(tt)...); } - 它包含了您想要的所有技巧,但我不认为这是“最佳实践”... - Aconcagua
1
@Aconcagua:无法经受const/volatile的更改,无论是引用(this)还是c-ellipsis用法(如printf)。 - Jarod42
1
参考该方法可能会有问题。 - Jarod42
显示剩余11条评论
3个回答

7

我会选择使用可变参数模板和完美转发。

class Log
{
public:
    Log(std::string str)
        : log(str)
    {}

    void print() const
    {
        puts(log.c_str());
    }

    template<typename... Ts>
    auto output(Ts&&... ts) const -> decltype(print(std::forward<Ts>(ts)...))
    {
        return print(std::forward<Ts>(ts)...);
    }

private:
    std::string log;
};

如果 print 的签名更改,则无需更改 output 中的任何内容(除了必须相应更改的 const)。唯一的问题是 output 签名的冗长和在返回类型中重复调用 print (在 C++14 中是不必要的)。好处是,即使添加另一个 print 的重载,它也可以正常工作!另一个 问题 是 IDE 不会转发文档注释。
另一个选择是引入引用该函数的成员变量。

确实有点啰嗦,但它做我需要的事情。 - stimulate
1
除了常量性("apart from constness"),你还需要考虑 volatilethis 的引用、C-ellipsis(如 printf)、noexcept、非可推导的模板参数(std::make_unique)。 - Jarod42

4
据我所知,该语言没有提供定义成员函数别名的机制。
然而,如果您愿意使用调用成员函数的非成员函数,则可以定义别名。
#include <string>
#include <stdio.h>

namespace MyApp
{
   class Log
   {
      public:
         Log(std::string str)
            : log(str)
         {}

         void print() const
         {
            puts(log.c_str());
         }

      private:
         std::string log;
   };

   void print(Log const& log)
   {
      log.print();
   }

   auto& output = print; // OK
   auto& write = print;  // OK
}

int main()
{
   MyApp::Log log("LOG: Log started.");
   MyApp::print(log);  // Works OK
   MyApp::output(log); // Works OK
   MyApp::write(log);  // Works OK
   return 0;
}

1
是的,我知道这个解决方案,但我希望别名的行为就像原始函数一样,也就是说,我可以使用 log.output() 调用它。 - stimulate
@stimulate,我认为你做不到。 - R Sahu
1
@stimulate 只是玩一下,基于这个答案,让你的原始方法起作用:class Log { public: static auto constexpr output = &Log::print; }; int main() { Log log; (log.*Log::output)(); } - 通过成员函数指针调用打印。虽然不完全符合你的问题,但仍然很好看(“我想学习”)。 - Aconcagua

1

你可以这样做,但是你提供的代码中存在一个非静态成员变量上的auto问题。

以下是一些示例,但调用成员并不美观。如果这是用户访问的API,我个人会选择包装打印。

class Log {
public:
    Log(std::string str)
            : log(str) {
    }

    void print() const {
        puts(log.c_str());
    }

    // Using auto.
    static constexpr auto output = &print;

    // Using function pointer.
    using print_ptr_t = void (Log::*)() const;
    print_ptr_t output2 = &Log::print;

private:
    std::string log;
};

Log l{ "test log" };
(l.*l.output)();
(l.*l.output2)();

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