我正在为未来的库编写一些通用代码。我在一个模板函数中遇到了以下问题。下面的代码:
template<class F>
auto foo(F &&f) {
auto result = std::forward<F>(f)(/*some args*/);
//do some generic stuff
return result;
}
它将正常工作,除非我向它传递一个返回void
的函数,如:
foo([](){});
现在,当然,我可以使用一些std::enable_if
魔术来检查返回类型,并为返回void
的函数执行特殊化,如下所示:
template<class F, class = /*enable if stuff*/>
void foo(F &&f) {
std::forward<F>(f)(/*some args*/);
//do some generic stuff
}
但这会严重地重复实际上逻辑上等效的函数的代码。对于void
返回函数和非void
返回函数,能否以一种通用的方式轻松地以优雅的方式完成这一点?
编辑:函数f()
和我想做的通用东西之间存在数据依赖,所以我不考虑这样的代码:
template<class F>
auto foo(F &&f) {
//do some generic stuff
return std::forward<F>(f)(/*some args*/);
}
5条答案
按热度按时间t3psigkw1#
如果你可以把“一些通用的东西”放在
bar
类的析构函数中(在一个安全的try/catch块中,如果你不确定不会抛出异常,就像Drax指出的那样),你可以简单地写:因此编译器计算
f(/*some args*/)
,执行b
的析构函数并返回计算值(或不返回)。注意
return func();
,其中func()
是返回void
的函数,是完全法律的的。4zcjmb1e2#
在某个地方,某种专业化是必要的。但这里的目标是避免专门化函数本身。但是,您可以专门化一个帮助器类。
使用gcc 9.1和
-std=c++17
进行了测试。b1payxdu3#
在我看来,做到这一点的最好方法是真正改变调用可能返回void的函数的方式。基本上,我们改变了返回
void
的类类型,而不是返回一些类类型Void
,也就是说,出于所有意图和目的,都是一样的,没有用户真的会在意。我们需要做的就是 Package 调用。下面的代码使用了C17的名字(
std::invoke
和std::invoke_result_t
),但它们都可以在C14中实现,没有太多的麻烦:这样做的好处是,你可以直接写你想写的代码开始,在你想写的方式:
你不必把所有的逻辑都放在析构函数中,也不必通过特殊化来复制所有的逻辑。代价是
foo([]{})
返回Void
,而不是void
,这并不是很大的代价。然后,如果Regular Void被采用,您所要做的就是将
invoke_void
替换为std::invoke
。6ovsh4lw4#
在C++中创建一个void函数(或非返回值函数),它将接受两个整数值作为参数,并将显示这两个数字之间的数字(包括两个数字)
fnx2tebb5#
如果你需要在“一些通用的东西”中使用
result
(在非void的情况下),我提出了一个基于if constexpr
的解决方案(不幸的是,在C++17之前不是)。老实说,不太优雅。
首先,检测
f
的“true返回类型”(给定参数)接下来是一个
constexpr
变量,以查看返回的类型是否为void
(只是为了简化以下代码)现在我们定义一个(潜在的)“假返回类型”:
int
当真实返回类型为void
时,则为TR_t
然后,我们将指向结果值的智能指针定义为指向“假返回类型”的指针(在void情况下为
int
)通过一个指针,而不是一个简单的“假返回类型”变量,我们也可以在
TR_t
不是默认可构造或不可赋值时进行操作(限制,由巴里指出(谢谢),这个答案的第一个版本)。现在,使用
if constexpr
,执行f
的两种情况(恕我直言,这是最糟糕的部分,因为我们必须编写两次相同的f
调用)在此之后,“一些通用的东西”可以使用
result
(在非void情况下)和isVoidTR)。 下一篇:
if constexpr`正如巴里所指出的,这个解决方案有一些重要的缺点,因为(不是
void
情况)return
TR_t
(f()
返回的类型)是引用类型,则根本不起作用无论如何,下面是一个完整的C++17编译示例