使用模板 C++ 检查类型

Checking type with template C++

我正在研究 Matrix class 的实现(在 Stroustrup 的书 TC++PL 第 4 版中有解释),但我无法真正理解某些段落。

我找到了这段代码:

文件 traits.h -> https://github.com/statslabs/matrix/blob/master/include/slab/matrix/traits.h

文件 matrix.h -> https://github.com/statslabs/matrix/blob/master/include/slab/matrix/matrix.h

在 matrix.h 中有一个函数(和许多其他函数一样)具有 Enable_if:

template<typename T, std::size_t N>
template<typename M, typename F>
Enable_if<Matrix_type<M>(), Matrix<T, N> &> Matrix<T, N>::apply(const M &m, F f) {
    /// Some code...
}

我认为 Enable_if 说:如果(M 是一个 Matrix),则将应用的 return 类型声明为 Matrix<T, N>&.

然后,我想知道 Matrix_type<M>() 是如何工作的,所以我转到 traits.h,然后我阅读:

struct substitution_failure {};

template <typename T>
struct substitution_succeeded : std::true_type {};

template <>
struct substitution_succeeded<substitution_failure> : std::false_type {};

template <typename M>
struct get_matrix_type_result {
  template <typename T, size_t N, typename = Enable_if<(N >= 1)>>
  static bool check(const Matrix<T, N> &m);

  template <typename T, size_t N, typename = Enable_if<(N >= 1)>>
  static bool check(const MatrixRef<T, N> &m);

  static substitution_failure check(...);

  using type = decltype(check(std::declval<M>()));
};

template <typename T>
struct has_matrix_type
    : substitution_succeeded<typename get_matrix_type_result<T>::type> {};

template <typename M>
constexpr bool Has_matrix_type() {
  return has_matrix_type<M>::value;
}

template <typename M>
using Matrix_type_result = typename get_matrix_type_result<M>::type;

template <typename M>
constexpr bool Matrix_type() {
  return Has_matrix_type<M>();
}

前 3 个结构描述了成功和失败的情况,template<>substitution_succeeded 的特化,表示:如果 substitution_succeeded 的类型是 substitution_failure,"return" 假否则 "return" 真。 希望我说的是对的。

现在,get_matrix_type_result 完全晦涩难懂。我不明白为什么它使用可变参数函数 (check(...)),declvaldecltype 在这段代码中做了什么,以及检查怎么可能 return a布尔值或 substitution_failure。为什么不只是 bool

谢谢。

Now, get_matrix_type_result is completely obscure. I can't understand why it use a variadic function (check(...)), what are declval and decltype doing in this code and how it is possible that check can return a bool or a "substitution_failure". Why not just bool?

重要的一点是 Enable_ifstd::enable_if 从 C++11 开始)旨在启用或禁用某些东西(模板函数、模板 class 特化、一个模板方法,一个模板变量特化)。

根据一个名为SFINAE(Substitution Failure Is Not An Error)的C++原则说,在某些地方,如果没有发生替换,那只是一个软错误,而不是硬错误,并且编译可以继续。

在你的例子中,type 定义如下

using type = decltype(check(std::declval<M>()));

其中 decltype() "return" 参数的类型;在这种情况下,类型 return 是从对 check() 的调用中编辑的,其中包含一个类型为 M 的假设对象(class 的模板参数)。

你可以写

using type = decltype(check(M{}));

传递一个M类型的对象。但这仅适用于默认可构造的类型。问题是:如果我们不知道如何构造泛型对象,如何在 decltype() 参数(具有推断类型的独有功能,而不是执行指令)中使用泛型对象?

解决方案是一个函数只声明(未定义)如下

template<class T>
typename std::add_rvalue_reference<T>::type declval() noexcept;

这是一个拥有类型 T(或更好:T &)对象的技巧,而且当您不知道如何构造它时。

回到check,你有它的三个版本(只声明:在decltype()中使用;我们只对returned类型感兴趣,所以没有需要执行它们,所以不需要定义它们):

1) 第一个接受 Matrix<T, N> (Enable_if) 如果 N >= 1

template <typename T, size_t N, typename = Enable_if<(N >= 1)>>
static bool check(const Matrix<T, N> &m);

如果你用 Matrix<T, 0> 调用 check()Enable_if return 什么都没有,所以你有一个替换失败(定义模板参数的默认值)所以这个check() 版本未启用

2) 第二个接受 MatrixRef<T, N> (Enable_if) 如果 N >= 1

template <typename T, size_t N, typename = Enable_if<(N >= 1)>>
static bool check(const MatrixRef<T, N> &m);

同样:如果你用 MatrixRef<T, 0> 调用 check()Enable_if return 什么都没有,所以你有一个替换失败(定义模板参数的默认值)所以这个版本的 check() 没有启用

3) 第三个接受一切并且 ever enabled

static substitution_failure check(...);

结论:

1) 如果 MMatrix<T, N>(或可转换为 Matrix<T, N> 的对象),对于某些 T 和某些 N N >= 1,编译器可以在 check() 的版本 (1) 和版本 (3) 之间进行选择,并选择版本 (1) 因为更专业,即 return bool,所以 type 变成 bool

2) 如果 MMatrixRef<T, N>(或可转换为 MatrixRef<T, N> 的对象),对于某些 T 和某些 N N >= 1,编译器可以在 check() 的版本 (2) 和版本 (3) 之间进行选择,并选择版本 (2) 因为更专业,即 return bool,所以 type 变成 bool

3) 如果 M 不能转换为 Matrix<T, N>MatrixRef<T, N>,使用 N >= 1,编译器只能选择版本 (3),即return一个substitution_failure,所以type变成substitution_failure.

题外话:您向我们展示的代码在我看来有点过于复杂。

举个例子,如果你重写get_matrix_type_result如下

template <typename M>
struct get_matrix_type_result {
  template <typename T, size_t N, typename = Enable_if<(N >= 1)>>
  static std::true_type check(const Matrix<T, N> &m);

  template <typename T, size_t N, typename = Enable_if<(N >= 1)>>
  static std::true_type check(const MatrixRef<T, N> &m);

  static std::false_type check(...);

  using type = decltype(check(std::declval<M>()));
};

你知道 type 是你在 has_matrix_type 中需要的类型,可以定义如下

template <typename T>
struct has_matrix_type
    : public get_matrix_type_result<T>::type
 { };

完全避免 substitution_failuresubstitution_succeded.

但是,也许,这样写代码是为了其他需要。