使用模板 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(...)
),declval
和 decltype
在这段代码中做了什么,以及检查怎么可能 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_if
(std::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) 如果 M
是 Matrix<T, N>
(或可转换为 Matrix<T, N>
的对象),对于某些 T
和某些 N
N >= 1
,编译器可以在 check()
的版本 (1) 和版本 (3) 之间进行选择,并选择版本 (1) 因为更专业,即 return bool
,所以 type
变成 bool
2) 如果 M
是 MatrixRef<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_failure
和 substitution_succeded
.
但是,也许,这样写代码是为了其他需要。
我正在研究 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(...)
),declval
和 decltype
在这段代码中做了什么,以及检查怎么可能 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_if
(std::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) 如果 M
是 Matrix<T, N>
(或可转换为 Matrix<T, N>
的对象),对于某些 T
和某些 N
N >= 1
,编译器可以在 check()
的版本 (1) 和版本 (3) 之间进行选择,并选择版本 (1) 因为更专业,即 return bool
,所以 type
变成 bool
2) 如果 M
是 MatrixRef<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_failure
和 substitution_succeded
.
但是,也许,这样写代码是为了其他需要。