繁体   English   中英

如何定义采用可变参数 class 模板的 function?

[英]How do I define a function that takes a variadic class template?

我正在尝试定义一个简单的基于变体的 Result 类型别名,有点像穷人的 rust-like Result 类型:

namespace detail {
template <typename SuccessType, typename... ErrorTypes>
struct Result {
  using type = std::variant<SuccessType, ErrorTypes...>;
};

template <typename... ErrorTypes>
struct Result<void, ErrorTypes...> {
  using type = std::variant<std::monostate, ErrorTypes...>;
};
}  // namespace detail

template <typename SuccessType, typename... ErrorTypes>
using Result_t = detail::Result<SuccessType, ErrorTypes...>::type;

Result_t只是一个std::variant ,其中第0个索引是成功的结果,而 rest 是错误结构。

我定义了这个辅助方法来检查结果是否良好:

template <typename SuccessType, typename... ErrorTypes>
inline bool Ok(const Result_t<SuccessType, ErrorTypes...>& r) {
  return r.index() == 0;
}

但是当我尝试实例化它时,我得到一个“没有找到匹配的重载 function”:

错误 C2672:“确定”:找不到匹配的重载 function

错误 C2783: 'bool Ok(const detail::Result<SuccessType,ErrorTypes...>::type &)': 无法推断出 'SuccessType' 的模板参数

struct FileError {};
struct BadJson {};

template <typename T>
using Result = Result_t<T, FileError, BadJson>;

Result<void> GetVoid() { return {}; }

TEST(ConfigFileTest, Result) {
  auto res = GetVoid();
  EXPECT_EQ(res.index(), 0);
  
  bool ok = Ok(res);
  EXPECT_TRUE(ok);
}

我究竟做错了什么? 如果我只是将Ok模板化为template <typename T> Ok(const T& r)它可以工作,但会使 function 过于笼统。

展开 function 参数中的Result_t别名后,如下所示:

template <typename SuccessType, typename... ErrorTypes>
bool Ok(const detail::Result<SuccessType, ErrorTypes...>::type& r) {
  return r.index() == 0;
}

这里有问题的部分是模板参数位于名称解析运算符::的左侧。 ::剩下的所有内容都是非推导上下文,这意味着它不用于推导模板 arguments。 因此,由于SuccessTypeErrorTypes...仅出现在非推导上下文中,因此无法推导它们,并且未明确指定它们的调用将失败。

您可以看到这条规则是必要的,因为理论上detail::Result<SuccessType, ErrorTypes...>的任何特化都可以具有与 arguments 类型匹配的::type type。 编译器无法检查每种可能的类型组合。

与其尝试给类型取别名,不如让Result成为一个实际的新类型:

template <typename SuccessType, typename... ErrorTypes>
struct Result {
  using variant_type = std::variant<SuccessType, ErrorTypes...>;
  variant_type variant;
};

template <typename... ErrorTypes>
struct Result<void, ErrorTypes...> {
  using variant_type = std::variant<std::monostate, ErrorTypes...>;
  variant_type variant;
};

template <typename SuccessType, typename... ErrorTypes>
bool Ok(const Result<SuccessType, ErrorTypes...>& r) {
  return r.variant.index() == 0;
}

或类似的规定。 如果您真的想使用仅使用别名的旧设计,那么 function 不应将嵌套别名作为参数,而是使用实际类型(这可能与设计意图不符):

template <typename T, typename... ErrorTypes>
bool Ok(const std::variant<T, ErrorTypes...>& r) {
  return r.index() == 0;
}

(我删除了模板上的内inline 。function 模板上的inline并没有多大意义。)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM