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的booltrue那样)。

成员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;
}