C++范型编程 -- <type_traits>头文件

在type_traits头文件中定义了许多非常好玩的东西,这里对着 c++ reference 翻译一丢丢

一,helper class , std::intergral_constant

template< class T, T v >
struct integral_constant;

我们知道对在模板参数中的非类型参数必须为常量,所以这个东西就是可以为类型T的任意一个常量v,做出来一个特定的类型,即integral_constant<T, v>的实例。通常用来保存int 和 bool,它的实现是这样的:

//声明一个为bool偏特化的模板
template <bool B>
using bool_constant = integral_constant<bool, B>;

//这个偏特化的模板总共就能实例化出两个具体的类型:
typedef std::integral_constant<bool, true> true_type
typedef std::integral_constant<bool, false> false_type

//可能的实现
template<class T, T v>
struct integral_constant {
    static constexpr T value = v;
    typedef T value_type;
    typedef integral_constant type;
    constexpr operator value_type() const noexcept { return value; }
    constexpr value_type operator()() const noexcept { return value; } //since c++14
};

那么,在模板编程中,计算都是在编译期搞定的, 作为模板参数的非类型参数必须为常量, 这也就说模板元编程中的变量一定是常量。。。如果要改变值只能在搞一个新的常量咯,

这个intergral_constant就能枚举完 T 中的任何一个常量。

二、主要的类型分类,以下的每个类型都有一个value成员,如果判断为真,则为true, 否则为false,这里挑几个简单介绍:

1, is_void, 用法 is_void<T>::value, 判断T是否为void类型, 一看就是用来判断函数返回值类型的

2, is_same<T1,T2> , 用法 is_same<T1, T2>::value, 判断两个类型是否一致,配合typedecl简直不要太爽。。。不过得注意typedecl的引用

。。。。。剩下的略

三、enable_if

原型:

template< bool B, class T = void >
struct enable_if;

用法, 如果B为true, 则 enable_if<B, T>会有一个类型成员,名为type, 类型为 T, 配合上面的各种判断,以及SFINAE 特性(无法实例化并不是错误),可以实现花式的自定义重载函数集:

// 1. the return type (bool) is only valid if T is an integral type:
template <class T>
typename std::enable_if<std::is_integral<T>::value,bool>::type
  is_odd (T i) {return bool(i%2);}

// 2. the second template argument is only valid if T is an integral type:
template < class T,
           class = typename std::enable_if<std::is_integral<T>::value>::type> //  这么写应该更容易明白: class T2 = typename std::enable_if<std::is_integral<T>::value>::type>
bool is_even (T i) {return !bool(i%2);}

int main() {

  short int i = 1;    // code does not compile if type of i is not integral

  std::cout << std::boolalpha;
  std::cout << "i is odd: " << is_odd(i) << std::endl;
  std::cout << "i is even: " << is_even(i) << std::endl;

  return 0;
}

或者更秀操作的这样:

#include <type_traits>
#include <iostream>
#include <string>

namespace detail { struct inplace_t{}; }
void* operator new(std::size_t, void* p, detail::inplace_t) {
    return p;
}

// #1, enabled via the return type
template<class T,class... Args>
typename std::enable_if<std::is_trivially_constructible<T,Args&&...>::value>::type
    construct(T* t,Args&&... args)
{
    std::cout << "constructing trivially constructible T\n";
}

// #2
template<class T, class... Args>
std::enable_if_t<!std::is_trivially_constructible<T,Args&&...>::value> //Using helper type
    construct(T* t,Args&&... args)
{
    std::cout << "constructing non-trivially constructible T\n";
    new(t, detail::inplace_t{}) T(args...);
}

// #3, enabled via a parameter
template<class T>
void destroy(T* t,
             typename std::enable_if<std::is_trivially_destructible<T>::value>::type* = 0)
{
    std::cout << "destroying trivially destructible T\n";
}

// #4, enabled via a template parameter
template<class T,
         typename std::enable_if<
             !std::is_trivially_destructible<T>{} &&
             (std::is_class<T>{} || std::is_union<T>{})
            >::type* = nullptr>
void destroy(T* t)
{
    std::cout << "destroying non-trivially destructible T\n";
    t->~T();
}

// #5, enabled via a template parameter
template<class T,
    typename = std::enable_if_t<std::is_array<T>::value> >
void destroy(T* t) // note, function signature is unmodified
{
    for(std::size_t i = 0; i < std::extent<T>::value; ++i) {
        destroy((*t)[i]);
    }
}
/*
template<class T,
    typename = std::enable_if_t<std::is_void<T>::value> >
void destroy(T* t){} // error: has the same signature with #5
*/

// the partial specialization of A is enabled via a template parameter
template<class T, class Enable = void>
class A {}; // primary template

template<class T>
class A<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
}; // specialization for floating point types

int main()
{
    std::aligned_union_t<0,int,std::string> u;

    construct(reinterpret_cast<int*>(&u));
    destroy(reinterpret_cast<int*>(&u));

    construct(reinterpret_cast<std::string*>(&u),"Hello");
    destroy(reinterpret_cast<std::string*>(&u));

    A<int> a1; // OK, matches the primary template
    A<double> a2; // OK, matches the partial specialization
}

上面的代码中,有一些会根据不同的条件来改变函数的签名,而有的则不会, 具体使用哪种应该看情况使用,比如调整重载候选函数集合时应该主动改变函数签名,而调整虚函数时则不应该改变函数签名

时间: 2024-08-08 09:36:36

C++范型编程 -- <type_traits>头文件的相关文章

linux网络编程常用头文件

sys/types.h:数据类型定义 sys/socket.h:提供socket函数及数据结构 netinet/in.h:定义数据结构sockaddr_in arpa/inet.h:提供IP地址转换函数 netdb.h:提供设置及获取域名的函数 sys/ioctl.h:提供对I/O控制的函数 sys/poll.h:提供socket等待测试机制的函数 其他在网络程序中常见的头文件 unistd.h:提供通用的文件.目录.程序及进程操作的函数 errno.h:提供错误号errno的定义,用于错误处理

使用预处理命令实现C的范型编程

一.引言 Generic programming is a style of computer programming in which algorithms are written in terms of types to-be-specified-later that are then instantiated when needed for specific types provided as parameters. 范型编程在1973年的ML语言中首次提出,是在C语言之后诞生的,你可以轻

C++编程常用头文件及其包含函数汇总

1. #include<iostream>是标准的C++头文件,任何符合标准的C++开发环境都有这个头文件. 当使用<iostream.h>时,相当于在c中调用库函数,使用的是全局命名空间,也就是早期的c++实现:当使用<iostream>的时候,该头文件没有定义全局命名空间,必须使用namespace std:这样才能正确使用cout.   2.#include <cstdlib>是C++里面的一个常用函数库, 等价于C中的<stdlib.h>

单片机中用c编程时头文件reg51.h及reg52.h解析

我们在用c语言编程是往往第一行就是reg51.h或者其他的自定义头文件,我们怎么样来理解呢? 1)“文件包含”处理. 程序的第一行是一个“文件包含”处理. 所谓“文件包含”是指一个文件将另外一个文件的内容全部包含进来.程序中包含REG51.h 文件的目的是为了要使用P1 (还有其他更多的符号)这个符号,即通知C 编译器,程序中所写的P1 是指80C51 单片机的P1 端口而不是其它变量.这是如何做到的呢? 打开reg51.h 可以看到这样的一些内容: (此文件一般在C:/KEIL/C51/INC

C/C++编程规范——头文件

在选择编程规范时,我首选google,其次是华为与微软,最后根据自身的一些实际情况进行调整.以下内容摘自google的c/c++编程规范.--------------------------通常,每一个.cc 文件(C++的源文件)都有一个对应的.h 文件(头文件),也有一些例外,如单元测试代码和只包含 main()的.cc 文件.正确使用头文件可令代码在可读性.文件大小和性能上大为改观.下面的规则将引导你规避使用头文件时的各种麻烦. 1. #define 的保护所有头文件都应该使用#defin

arpa/inet.h所引起的Segmentation fault及网络编程常见的头文件

最近在学习Linux网络编程方面的知识,感觉还是有些困难.主要是对协议过程的理解,还有socket的API的理解不够深刻.今天复习编写了一个TCP的服务端和客户端的程序实现client.c从命令行参数中获得一个字符串发给服务器,然后接收服务器返回的已处理的字符串并打印. server.c 1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <unistd.h>

《七周七语言:理解多种编程范型》のIo课后习题答案

哎,因为上周忙着写OAuth2.0服务端框架草稿 ,耽误了一周学习Io Language了. 本篇习题解答是接着 <七周七语言:理解多种编程范型>のRuby课后习题答案 Io是原型语言,类似于JavaScript,并不区别类和对象,所有的东东都是对象,对象的数据结构就是由键值表来维护的(在Io中就是所谓的槽),通过各种消息传递给对象来实现打印输出,复制对象等功能.因为语法非常简单(也木有语法糖),所以你可以尽情构建自己的库和功能. 第一天: 1. 对1+1求值,然后对1+"one&q

google C++编程风格指南之头文件的包含顺序

google C++编程风格对头文件的包含顺序作出如下指示: (1)为了加强可读性和避免隐含依赖,应使用下面的顺序:C标准库.C++标准库.其它库的头文件.你自己工程的头文件.不过这里最先包含的是首选的头文件,即例如a.cpp文件中应该优先包含a.h.首选的头文件是为了减少隐藏依赖,同时确保头文件和实现文件是匹配的.具体的例子是:假如你有一个cc文件(linux平台的cpp文件后缀为cc)是google-awesome-project/src/foo/internal/fooserver.cc,

linux编程头文件所在路径的问题

一.问题引入 1.头文件与库 当我们在PC主机linux环境下(如ubuntu),编写linux应用程序,然后利用gcc来编译.在源代码的开始位置会写入头文件,那是因为我们使用了系统提供的库函数,例如printf.open.read.write等等.我们会写入类似的内容: #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> ...... 我们的应用程序代码