我正在研究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
说:如果(M
是Matrix
),则将应用的返回类型声明为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(...)
),此代码中declval
和decltype
的功能以及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)如果
M
是Matrix<T, N>
(或可转换为Matrix<T, N>
的对象),则对于某些T
和某些带有N
的N >= 1
,编译器可以在check()
的版本(1)和版本(3)之间进行选择,并选择版本(1),因为更多专门的,返回bool
,所以type
变成bool
2)如果
M
是MatrixRef<T, N>
(或可转换为MatrixRef<T, N>
的对象),则对于某些T
和某些带有N
的N >= 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>()));
};
您拥有
type
是has_matrix_type
中所需的类型,可以如下定义template <typename T>
struct has_matrix_type
: public get_matrix_type_result<T>::type
{ };
完全避免
substitution_failure
和substitution_succeded
。但是,也许,这种代码是为其他需求编写的。