我正在研究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
,则“返回”否,否则“返回” true。
我希望我说的是正确的。
现在,get_matrix_type_result
完全模糊了。我无法理解为什么它使用可变参数(check(...)
),declval
和decltype
在此代码中的作用以及校验可能返回bool或{ {1}}。为什么不只是substitution_failure
?
谢谢。
答案 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>
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 >= 1
或check()
,则使用bool
时,编译器只能选择版本(3),该版本将返回type
,因此bool
成为M
。
主题外:在我看来,您显示给我们的代码有些复杂。
例如,如果您按照以下方式重写Matrix<T, N>
MatrixRef<T, N>
N >= 1
是您在substitution_failure
中需要的类型,可以定义如下
type
完全避免substitution_failure
和get_matrix_type_result
。
但是,也许,代码是出于其他需要编写的。