首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >返回void或非void类型的函数参数

返回void或非void类型的函数参数
EN

Stack Overflow用户
提问于 2019-05-22 20:18:10
回答 3查看 1.6K关注 0票数 18

我正在为未来的库编写一些泛型代码。我在一个模板函数中遇到了以下问题。考虑下面的代码:

代码语言:javascript
复制
template<class F>
auto foo(F &&f) {
    auto result = std::forward<F>(f)(/*some args*/);
    //do some generic stuff
    return result;
}

它可以很好地工作,除非我向它传递一个返回void的函数,如下所示:

代码语言:javascript
复制
foo([](){});

当然,现在我可以使用一些std::enable_if魔术来检查返回类型,并对返回如下所示的void的函数执行专门化:

代码语言:javascript
复制
template<class F, class = /*enable if stuff*/>
void foo(F &&f) {
    std::forward<F>(f)(/*some args*/);
    //do some generic stuff
}

但是,对于实际上逻辑上等价的函数,这将严重地重复代码。对于void-returning和非void-returning函数,这能以一种优雅的方式以通用的方式轻松完成吗?

编辑:在函数f()和我想要做的泛型内容之间有数据依赖关系,所以我没有考虑这样的代码:

代码语言:javascript
复制
template<class F>
auto foo(F &&f) {
    //do some generic stuff
    return std::forward<F>(f)(/*some args*/);
}
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2019-05-22 20:21:48

如果您可以将“一些泛型内容”放在bar类的析构函数中(在安全的try/catch块中,如果您不确定这不会抛出异常,正如Drax所指出的那样),那么您可以简单地编写

代码语言:javascript
复制
template <typename F>
auto foo (F &&f)
 {
   bar b;

   return std::forward<F>(f)(/*some args*/);
 }

因此,编译器计算f(/*some args*/),执行b的析构函数,并返回计算值(或不返回任何值)。

注意到return func();是完全合法的,其中func()是返回void的函数。

票数 14
EN

Stack Overflow用户

发布于 2019-05-22 20:36:08

在某些地方,一些专业化是必要的。但这里的目标是避免专门化函数本身。但是,您可以专门化帮助器类。

使用带有-std=c++17的gcc 9.1进行了测试。

代码语言:javascript
复制
#include <type_traits>
#include <iostream>

template<typename T>
struct return_value {


    T val;

    template<typename F, typename ...Args>
    return_value(F &&f, Args && ...args)
        : val{f(std::forward<Args>(args)...)}
    {
    }

    T value() const
    {
        return val;
    }
};

template<>
struct return_value<void> {

    template<typename F, typename ...Args>
    return_value(F &&f, Args && ...args)
    {
        f(std::forward<Args>(args)...);
    }

    void value() const
    {
    }
};

template<class F>
auto foo(F &&f)
{
    return_value<decltype(std::declval<F &&>()(2, 4))> r{f, 2, 4};

    // Something

    return r.value();
}

int main()
{
    foo( [](int a, int b) { return; });

    std::cout << foo( [](int a, int b) { return a+b; }) << std::endl;
}
票数 9
EN

Stack Overflow用户

发布于 2019-05-22 22:12:58

在我看来,要做到这一点,最好的方法是实际改变调用可能返回void的函数的方式。基本上,我们将返回void的类改为返回某种类类型Void,也就是说,无论出于何种目的,都是一样的,没有用户会真正关心。

代码语言:javascript
复制
struct Void { };

我们所需要做的就是包装调用。下面的代码使用了C++17名称(std::invokestd::invoke_result_t),但它们都可以在C++14中实现,不用太费事:

代码语言:javascript
复制
// normal case: R isn't void
template <typename F, typename... Args, 
    typename R = std::invoke_result_t<F, Args...>,
    std::enable_if_t<!std::is_void<R>::value, int> = 0>
R invoke_void(F&& f, Args&&... args) {
    return std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
}

// special case: R is void
template <typename F, typename... Args, 
    typename R = std::invoke_result_t<F, Args...>,
    std::enable_if_t<std::is_void<R>::value, int> = 0>
Void invoke_void(F&& f, Args&&... args) {
    // just call it, since it doesn't return anything
    std::invoke(std::forward<F>(f), std::forward<Args>(args)...);

    // and return Void
    return Void{};
}

这样做的好处是,你可以直接编写你想要写的代码,以你想要的方式开始:

代码语言:javascript
复制
template<class F>
auto foo(F &&f) {
    auto result = invoke_void(std::forward<F>(f), /*some args*/);
    //do some generic stuff
    return result;
}

而且,您不必将所有逻辑都放在析构函数中,也不必通过专门化来复制所有逻辑。代价是foo([]{})返回Void而不是void,这并不是很大的开销。

然后,如果Regular Void被采用,您所要做的就是用std::invoke替换invoke_void

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/56256640

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档