为什么不能在范围内声明模板变量或模板 typedef?
我想像这样用c++17写一段代码
auto foo = [](auto fun, auto... x) {
template <typename T>
using ReturnType = std::invoke_result_t<fun, T>;
if constexpr (!(std::is_same_v<void, ReturnType<decltype(x)>> || ... ||
false)) {
return std::tuple<ReturnType<decltype(x)>...>(fun(x)...);
} else {
(fun(x), ...);
return;
}
};
这段代码定义了一个函数foo
它需要一个函数 fun
和一堆论点x...
.如果所有返回类型 fun(x)
不是 void
然后返回一个结果元组。如果至少一种返回类型是 void
然后调用所有函数但返回 void
.
在这个简单的例子中,我当然可以替换 ReturnType<decltype(x)>
与 decltype(fun(x))
,但在我的用例中,实际类型要复杂得多,上面的代码仅作为动机。
另外,我讨厌写作 ReturnType<decltype(x)>
.我更喜欢写 ReturnType(x)
,但这可能是不可能的。
我不喜欢的解决方案: 在函数外定义模板 typedef 为
template<typename Fun, typename T>
using ReturnType = std::invoke_result_t<Fun,T>;
然后在函数中使用
ReturnType<decltype(fun),delctype(x)>
时间越来越长,我必须将每个本地类型都作为模板参数。
最佳答案
代码实际上更简单,没有引入任何助手:
if constexpr ((!std::is_void_v<decltype(fun(x))> && ...)) {
return std::tuple(fun(x)...);
} else {
(fun(x), ...);
}
&&
和 ||
具有空包的默认值(分别为 true
和 false
),因此您不必将它们转换为一元运算符。而且你不需要 invoke_result_t
因为你只是直接打电话。即使你这样做了:
using F = decltype(fun);
if constexpr ((!std::is_void_v<std::invoke_result_t<F, decltype(x))> && ...)) {
return std::tuple(std::invoke(fun, x)...);
} else {
(fun(x), ...);
}
不久。
也就是说,我发现这个构造不是很有用 - 考虑到您对 void
得到的结果截然不同和非 void
个案。也许f(x)
仍然是 X
但是f(y)
是void
,我们会得到 foo(x,x)
正在tuple<X,X>
但是foo(x,y)
正在void
?很难编码。
我建议不要删除所有返回类型,而是解决损坏的类型。如:
struct Void { };
template <typename F, typename... Args,
typename R = std::invoke_result_t<F, Args...>,
REQUIRES(std::is_void_v<R>)>
Void invoke_void(F&& f, Args&&... args) {
std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
return Void{};
}
template <typename F, typename... Args,
typename R = std::invoke_result_t<F, Args...>,
REQUIRES(!std::is_void_v<R>)>
R invoke_void(F&& f, Args&&... args) {
return std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
}
现在,我们可以随时调用函数并返回它:
auto foo = [](auto fun, auto... x) {
return std::tuple(invoke_void(fun, x)...);
};
关于c++ - 范围内的模板变量或模板 typedef,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49214517/