一、说明
c++标准中提供了元编程接口std::integral_constant,而在这个接口中又提供了针对bool类型的两个实例化类型:
c
true_type std::integral_constant<bool, true>
false_type std::integral_constant<bool, false>
在前面的文章分析中,特别是针对std::index_sequence的相关文章中,对其进行过介绍。另外在分析其它模板和元编程的过程中,也对它们进行过顺便的应用说明。但对它们的应用并没有系统性的说明。本文将针对其实际应用的方式进行整体的总结说明。
二、应用形式
在模板或元编程中,true_type和false_type基本有两种应用形式:
- 用作基类
这种是最典型的情况,比如前面的成员检测:
c
template<typename T, typename = void>
struct checkSize : std::false_type {};
template<typename T>
struct checkSize<T, std::void_t<decltype(std::declval<T>().size())>> : std::true_type {};
而在元编程库中的很多接口都继承自它们,比如std::is_pointer等:
c
template<class T>
struct is_pointer : std::false_type {};
template<class T>
struct is_pointer<T*> : std::true_type {};
template<class T>
struct is_pointer<T* const> : std::true_type {};
template<class T>
struct is_pointer<T* volatile> : std::true_type {};
template<class T>
struct is_pointer<T* const volatile> : std::true_type {};
在实际的开发中,这种情况也是非常常见的。
- 标签分发处理
这种情况主要有函数重载处理、静态断言和条件编译:
c
#include <iostream>
#include <type_traits>
#include <utility>
struct CopyType {};
struct MoveType {
MoveType() = default;
MoveType(const MoveType &) = delete;
MoveType(MoveType &&) = default;
};
template <typename T> void testFunc(T value, std::true_type ) {
std::cout << "call copy!" << std::endl;
}
template <typename T> void testFunc(T value, std::false_type ) {
std::cout << "call move!" << std::endl;
}
template <typename T> void process(T value) {
testFunc(std::forward<T>(value), std::is_copy_constructible<T>{});
}
int main() {
CopyType ct;
process(ct);
MoveType nct;
process(std::move(nct));
return 0;
}
其它的实现也都类似,不再过多列举。
三、实现方法
std::false_type和std::true_type是从std::integral_constant扩展而来的,所以需要看integral_constant具体的实现:
c
template<class T, T v>
struct integral_constant
{
static constexpr T value = v;
using value_type = T;
using type = integral_constant; // using injected-class-name
constexpr operator value_type() const noexcept { return value; }
constexpr value_type operator()() const noexcept { return value; } // since c++14
};
template< bool B >
using bool_constant = integral_constant<bool, B>;
using true_type = integral_constant<bool, true>;
using false_type =integral_constant<bool, false>;
大家可以尝试着用不同的方式来实现并进行工程验证。
四、例程
下面给出一个例程供大家分析:
c
#include <iostream>
#include <type_traits>
// 正确转换函数
template <typename F, typename T>
void castType(const F &from, T &to, std::true_type) {
to = static_cast<T>(from);
std::cout << "cast ok!" << std::endl;
}
//错误处理函数
template <typename F, typename T>
void castType(const F &from, T &to, std::false_type) {
static_assert(std::is_convertible<F, T>::value, "convertible result");
std::cout << "cast error" << std::endl;
}
template <typename F, typename T>
void castCheckRun(const F &from, T &to) {
castType(from, to, std::is_convertible<F, T>{});
}
int main() {
int d = 1;
double dd = 0.0;
std::string str = "abc";
castCheckRun(d, dd);
//castCheckRun(d, str); //error
return 0;
}
再看一下std::is_function的源码实现:
c
template<class>
struct is_function : std::false_type {};
// specialization for regular functions
template<class Ret, class... Args>
struct is_function<Ret(Args...)> : std::true_type {};
// specialization for variadic functions such as std::printf
template<class Ret, class... Args>
struct is_function<Ret(Args......)> : std::true_type {};
// specialization for function types that have cv-qualifiers
template<class Ret, class... Args>
struct is_function<Ret(Args...) const> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args...) volatile> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args...) const volatile> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args......) const> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args......) volatile> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args......) const volatile> : std::true_type {};
// specialization for function types that have ref-qualifiers
template<class Ret, class... Args>
struct is_function<Ret(Args...) &> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args...) const &> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args...) volatile &> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args...) const volatile &> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args......) &> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args......) const &> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args......) volatile &> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args......) const volatile &> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args...) &&> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args...) const &&> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args...) volatile &&> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args...) const volatile &&> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args......) &&> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args......) const &&> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args......) volatile &&> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args......) const volatile &&> : std::true_type {};
// specializations for noexcept versions of all the above (C++17 and later)
template<class Ret, class... Args>
struct is_function<Ret(Args...) noexcept> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args......) noexcept> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args...) const noexcept> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args...) volatile noexcept> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args...) const volatile noexcept> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args......) const noexcept> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args......) volatile noexcept> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args......) const volatile noexcept> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args...) & noexcept> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args...) const & noexcept> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args...) volatile & noexcept> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args...) const volatile & noexcept> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args......) & noexcept> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args......) const & noexcept> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args......) volatile & noexcept> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args......) const volatile & noexcept> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args...) && noexcept> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args...) const && noexcept> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args...) volatile && noexcept> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args...) const volatile && noexcept> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args......) && noexcept> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args......) const && noexcept> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args......) volatile && noexcept> : std::true_type {};
template<class Ret, class... Args>
struct is_function<Ret(Args......) const volatile && noexcept> : std::true_type {};
std::is_function处理的原理基本是函数类型(对象)不能有cv限定符且不能绑定到非const的左值引用(可绑定到const的左传引用或右值引用)。当然实际应用时一般是使用函数在地址转换时会隐式的转为函数指针。不过看上面的代码可以发现,其实为了编译处理更简单,就是靠量大管饱,大量特化相关的函数判断即可。
也可以这样理解,通过模板偏特化(partial specialization)列出可能的全部函数类型签名形式,对合法的函数类型进行std::true_type特化;其它由非函数类型则由匹配主模板,特化为 std::false_type。
五、总结
元编程和模板编程的复杂是大家公认的,但只要大家从基础的知识点出发,将不同的基础知识灵活运用起来,再加上多看网上开源的好的工程代码,就能够较快的掌握元编程和模板编程的技术。与诸君共勉!