作用域中的模板变量或模板类型定义

Template variable or template typedef in a scope

本文关键字:类型 定义 变量 作用域      更新时间:2023-10-16

为什么模板变量或模板类型定义不能在作用域内声明?

我想用 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该函数x... fun和一堆参数。如果未void所有返回类型fun(x)则返回结果元组。如果至少void一种返回类型,则只需调用所有函数,但返回void

在这个简单的示例中,我当然可以用decltype(fun(x))替换ReturnType<decltype(x)>,但在我的用例中,实际类型要复杂得多,上面的代码仅作为动机。

另外,我讨厌写ReturnType<decltype(x)>.我更喜欢写ReturnType(x),但这可能是不可能的。


我不喜欢的解决方案:将函数外部的模板类型定义

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), ...);
}

&&|| 具有空包的默认值(分别为 truefalse(,因此您不必将它们转换为一元运算符。而且您不需要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)仍然是一个Xf(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)...);
};