C++编程实践—false_type和true_type的实践应用

一、说明

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基本有两种应用形式:

  1. 用作基类
    这种是最典型的情况,比如前面的成员检测:
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 {};

在实际的开发中,这种情况也是非常常见的。

  1. 标签分发处理
    这种情况主要有函数重载处理、静态断言和条件编译:
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。

五、总结

元编程和模板编程的复杂是大家公认的,但只要大家从基础的知识点出发,将不同的基础知识灵活运用起来,再加上多看网上开源的好的工程代码,就能够较快的掌握元编程和模板编程的技术。与诸君共勉!

相关推荐
嘿BRE1 天前
【C++】智能指针
c++
楼田莉子1 天前
Linux学习之库的原理与制作
linux·运维·服务器·c++·学习
浅念-1 天前
C++第一课
开发语言·c++·经验分享·笔记·学习·算法
charlie1145141911 天前
现代嵌入式C++教程:对象池(Object Pool)模式
开发语言·c++·学习·算法·嵌入式·现代c++·工程实践
HABuo1 天前
【linux进程控制(三)】进程程序替换&自己实现一个bash解释器
linux·服务器·c语言·c++·ubuntu·centos·bash
一只小bit1 天前
Qt 多媒体:快速解决音视频播放问题
前端·c++·qt·音视频·cpp·页面
凯子坚持 c1 天前
C++大模型SDK开发实录(二):DeepSeek模型接入、HTTP通信实现与GTest单元测试
c++·http·单元测试
uoKent1 天前
c++中的运算符重载
开发语言·c++
量子炒饭大师1 天前
【C++入门】面向对象编程的基石——【类与对象】基础概念篇
java·c++·dubbo·类与对象·空指针规则
MSTcheng.1 天前
【C++】链地址法实现哈希桶!
c++·redis·哈希算法