使用模板C ++检查类型

时间:2019-07-12 19:48:42

标签: c++ c++11 matrix sfinae decltype

我正在研究Matrix类的实现(在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说:如果(MMatrix),则将申请的返回类型声明为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,则“返回”否,否则“返回” true。 我希望我说的是正确的。

现在,get_matrix_type_result完全模糊了。我无法理解为什么它使用可变参数(check(...)),declvaldecltype在此代码中的作用以及校验可能返回bool或{ {1}}。为什么不只是substitution_failure

谢谢。

1 个答案:

答案 0 :(得分:2)

  

现在,get_matrix_type_result完全模糊了。我不明白为什么它使用可变参数函数(check(...)),这段代码中的declval和decltype做什么,以及check可能返回bool或“ substitution_failure”的可能性。为什么不只是布尔?

重要的一点是Enable_if(从C ++ 11开始的std::enable_if)旨在启用或不启用某些功能(模板功能,模板类专门化,模板方法,模板变量专业化。)

地面上有一种名为SFINAE(替代失败不是错误)的C ++原则,它说,在某些地方,如果不进行替换,则仅是软错误,不是硬错误,并且是编译可以继续。

对于您来说,type的定义如下

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

其中decltype()“返回”参数的类型;在这种情况下,该类型是通过调用类型为check()的假设对象(该类的模板参数)返回到M的。

您可以写

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

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

解决方案是仅按以下方式声明(未定义)的函数

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

在您不知道如何构造它的情况下,拥有类型T(或更优:T &)的对象也很容易。

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

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);

如果您使用check()调用Matrix<T, 0>,则Enable_if不返回任何内容,因此替换失败(为模板参数定义了默认值),因此此版本的{{1} }未启用

2)第二个接受check(),但如果接受MatrixRef<T, N>

,则仅接受{strong> (Enable_if
N >= 1

再次:如果您使用template <typename T, size_t N, typename = Enable_if<(N >= 1)>> static bool check(const MatrixRef<T, N> &m); 调用check(),则MatrixRef<T, 0>不返回任何内容,因此您将出现替换失败(为模板参数定义默认值),因此此版本的{{ 1}}未启用

3)第三个接受所有内容,并且曾经 启用

Enable_if

结论:

1)如果check()static substitution_failure check(...); (或可转换为M的对象),则对于某些Matrix<T, N>和某些Matrix<T, N>具有{{1 }},编译器可以在T的版本(1)和版本(3)之间进行选择,并选择版本(1),因为它更加专业,返回N,因此N >= 1成为{{ 1}}

2)如果check()bool(或可转换为type的对象),则对于某些bool和某些M具有{{1 }},编译器可以在MatrixRef<T, N>的版本(2)和版本(3)之间进行选择,并选择版本(2),因为它更加专业,返回MatrixRef<T, N>,因此T成为{{ 1}}

3)如果N无法转换为N >= 1check(),则使用bool时,编译器只能选择版本(3),该版本将返回type,因此bool成为M

主题外:在我看来,您显示给我们的代码有些复杂。

例如,如果您按照以下方式重写Matrix<T, N>

MatrixRef<T, N>

N >= 1是您在substitution_failure中需要的类型,可以定义如下

type

完全避免substitution_failureget_matrix_type_result

但是,也许,代码是出于其他需要编写的。