我正在研究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,则“返回”为false,否则为“返回” true。
我希望我说的是正确的。

现在,get_matrix_type_result完全模糊了。我不明白为什么它要使用可变参数函数(check(...)),此代码中declvaldecltype的功能以及check可能返回bool或substitution_failure的可能性。为什么不只是bool

谢谢。

最佳答案



重要的一点是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不返回任何内容,因此您将发生替换失败(定义了模板参数的默认值),因此未启用此版本的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);

再次:如果您使用check()调用MatrixRef<T, 0>,则Enable_if不返回任何内容,因此您将发生替换失败(为模板参数定义默认值),因此此版本的check()不启用

3)第三个接受一切,并且启用 启用
static substitution_failure check(...);

结论:

1)如果MMatrix<T, N>(或可转换为Matrix<T, N>的对象),则对于某些T和某些带有NN >= 1,编译器可以在check()的版本(1)和版本(3)之间进行选择,并选择版本(1),因为更多专门的,返回bool,所以type变成bool
2)如果MMatrixRef<T, N>(或可转换为MatrixRef<T, N>的对象),则对于某些T和某些带有NN >= 1,编译器可以在check()的版本(2)和版本(3)之间进行选择,并选择版本(2),因为更多专门的,返回bool,所以type变成bool
3)如果M不能转换为Matrix<T, N>MatrixRef<T, N>,则使用N >= 1,编译器只能选择返回substitution_failure的版本(3),因此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>()));
};

您拥有typehas_matrix_type中所需的类型,可以如下定义
template <typename T>
struct has_matrix_type
    : public get_matrix_type_result<T>::type
 { };

完全避免substitution_failuresubstitution_succeded

但是,也许,这种代码是为其他需求编写的。

09-30 15:30
查看更多