C++模板元编程之type_traits
前言
主要介绍各种技法的实现、应用等
integral_constant和bool_constant
integral_constant(C++11)和bool_constant(C++17)是其中不少类的基类,下面给出integral_constant的简要实现(MSVC)
// C++11
// 后面会提到每行代码的具体意义,因此看不懂也没关系
template <class _Ty, _Ty _Val>
struct integral_constant {
static constexpr _Ty value = _Val;
using value_type = _Ty;
using type = integral_constant;
constexpr operator value_type() const noexcept {
return value;
}
constexpr value_type operator()() const noexcept {
return value;
}
};
根据integral_constant,我们可以引出bool_constant这一辅助模板,它对integral_constant做了一些包装,实现如下:
// 使用到C++11的别名模板(alias template)
template <bool _Val>
using bool_constant = integral_constant<bool, _Val>;
using true_type = bool_constant<true>;
using false_type = bool_constant<false>;
现在,我们可以使用true_type和false_type了,true_type等价于:true_type =>bool_constant<true> => integral_constant<bool, true>
现在回到之前所说的integral_constant实现,其第一个模板参数为类型,第二个模板参数为一个值(正如true_type的bool
和true
那样)。
成员value
对应第二个模板参数,其一般用来存储结果。比如,我们常常这样用:
std::cout << std::is_enum<int>::value << '\n';
std::cout << std::is_array<int[3]>::value << '\n';
std::cout << std::is_class<int>::value << '\n';
在上述例子中,类型的“计算结果”将被保存到value中。
这里有两个成员函数可能让人比较疑惑,先看第一个:
constexpr operator value_type() const noexcept { return value;}
这是一个转换函数,用来返回包装后的值。下面的情况将调用此函数
bool a1 = true_type(); // a1 == true
// bool a2 = true_type::value;
// 相比于这种方式更加简便
// 或者是这样
true_type t1;
bool a2 = static_cast<bool>(t1) // a2 == true
第二个函数也是用来返回value。不过,它被包装成可以调用的函数形式:
constexpr value_type operator()() const noexcept {return value;}
下面的操作将会调用这一函数:
true_type a;
auto result = a(); // result == true
应用
true_type和false_type实际上是两个最常用的应用(bool_constant),在其他类的使用中,常常用来对类的类型进行判断。
还有一种应用是编译期分支预测(tag-dispatching),还可以配合三目运算符使用,大概长这样:
// std::integral_constant<int,0>::type => 类型为 std::integral_constant<int,0>
void gotoN_impl(std::integral_constant<int,0>::type)
{
std::cout << "GoTo 0" << '\n';
}
void gotoN_impl(std::integral_constant<int,1>::type)
{
std::cout << "GoTo 1" << '\n';
}
void gotoN_impl(std::integral_constant<int,2>::type)
{
std::cout << "GoTo 2" << '\n';
}
void gotoN_impl(std::integral_constant<int,3>::type)
{
std::cout << "GoTo 3" << '\n';
}
template<int N>
void gotoN()
{
gotoN_impl(typename std::integral_constant<int, N>::type());
}
int main()
{
gotoN<0>();
gotoN<1>();
gotoN<2>();
gotoN<3>();
constexpr auto x = 99;
gotoN< x<4 ? x : 3 >();
}
判断是否为智能指针
template <class _Ty, _Ty _Val>
struct my_integral_constant {
static constexpr _Ty value = _Val;
using value_type = _Ty;
using type = my_integral_constant;
constexpr operator value_type() const noexcept {
return value;
}
constexpr value_type operator()() const noexcept {
return value;
}
};
template <bool _Val>
using my_bool_constant = my_integral_constant<bool, _Val>;
using my_true_type = my_bool_constant<true>;
using my_false_type = my_bool_constant<false>;
template <typename T>
constexpr bool is_smart_pointer_v = false;
template <typename T>
constexpr bool is_smart_pointer_v<shared_ptr<T> > = true;
template <typename T>
constexpr bool is_smart_pointer_v<unique_ptr<T> > = true;
template <typename T>
constexpr bool is_smart_pointer_v<weak_ptr<T> > = true;
template <typename T>
constexpr bool is_smart_pointer_v<auto_ptr<T> > = true;
template <class _Ty>
struct is_smart_pointer : my_bool_constant<is_smart_pointer_v<_Ty>> {};
int main() {
int* p(new int(2));
shared_ptr<int> pp(new int(2));
unique_ptr<int> upp(new int(4));
std::cout << is_smart_pointer<decltype(pp)>::value << '\n';
std::cout << is_smart_pointer<decltype(upp)>::value << '\n';
return 0;
}