【问题标题】:How to use templated methods to take a function as input which can be used with every signature如何使用模板化方法将函数作为输入,该函数可用于每个签名
【发布时间】:2021-04-08 05:42:31
【问题描述】:

我有一个类,它的任务是在异步上下文中运行函数,因此可以将函数(或方法)作为输入。

现在我保留了两个具有不同名称的函数以将函数作为输入,因为我需要区分 void 返回类型和其他返回类型。将这两个函数命名为相同对我不起作用。我想要的是两者具有相同的名称,并且函数的调用者不需要考虑返回类型,如果他愿意并且可能的话,他可以得到它:

#include <functional>

template<typename Func, typename ...Args>
auto Foo(Func f, Args&&... args)
{
    decltype(f(std::forward<Args>(args)...)) ret; 
    init();
    ret = f(std::forward<Args>(args)...);
    clean();
    return ret;
}

template<typename Func, typename ...Args>
void FooVoid(Func f, Args&&... args)
{
    init();
    f(std::forward<Args>(args)...);
    clean();
}

int Bar(){return 0;}

void Empty() {}

struct A
{
    int Bar(){return 0;}
    void Empty(){}
};



int main()
{
    auto bar = Foo(Bar);
    FooVoid(Empty);

    A a;
    bar = Foo(std::bind(&A::Bar, a));
    FooVoid(std::bind(&A::Empty, a));

    return 0;
}

我想要的是两个函数可以具有相同的名称而不会产生歧义。目前没有其他限制。我怎么能做到这一点?关于代码的任何其他建议也会有所帮助。我是这个话题的新手!

【问题讨论】:

    标签: c++ templates variadic-templates


    【解决方案1】:

    从 C++17 开始,您可以使用 if constexpr。在条件下,您检查函数f 是否具有void 以外的返回类型,如果是,则调用该函数并返回其值。否则,auto 作为返回类型将被推断为 void - false-statement 中没有返回。

    template<typename Func, typename ...Args>
    auto Foo(Func f, Args&&... args)
    {
        if constexpr (!std::is_same_v<void,decltype(f(std::forward<Args>(args)...))> )
        {
            init();
            auto ret =  f(std::forward<Args>(args)...);
            clean();
            return ret;
        }
        else {
            init();
            f(std::forward<Args>(args)...);
            clean();
        }
    }
    

    Demo

    【讨论】:

    • 好在不久的将来我们将把 C++17 作为标准,我想如果没有其他答案我会使用这个
    【解决方案2】:

    对于 RAII 对象,您可以使用相同的实现:

    template<typename Func, typename ...Args>
    auto Foo(Func f, Args&&... args)
    {
        struct Raii
        {
            Raii() { init(); }
            ~Raii() { clean(); }
        } raii;
        return f(std::forward<Args>(args)...);
    }
    

    Demo

    注意:在 void-return-type 函数中允许返回 void。 :-)

    【讨论】:

    • 不错的解决方案,我也喜欢用RAII来做这样的事情。在那种情况下,我认为它可能会错过真正需要发生的事情,在函数执行之后,因为乍一看,我认为函数的执行是最后发生的事情
    • Raii 更好的命名可能会有所帮助 :-) (init/clean 对我来说太通用了,无法使用非通用名称,但是使用适当的资源名称,这似乎很自然该 return 是最后一条语句)。另一种方法是对init(); Finally at_end_scope([](){clean();}) 进行不同的拆分(FinallyRaii 的变体,它在销毁时执行 lambda,“类似于”go 中的defer)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多