模板元编程

C++高阶必会操作--模板元编程 泛型编程大家应该都很熟悉了,主要就是利用模板实现“安全的宏”,而模板元编程区别于我们所知道的泛型编程,它是一种较为复杂的模板,属于C++的高阶操作了,它最主要的优点就在于把计算过程提前到编译期,能带来可观的性能提升。接下来,一起来学习吧。 1、概述 模板元编程(Template Meta programming,TMP)是编写生成或操纵程序的程序,也是一种复杂且功能强大的编程范式(Programming Paradigm)。 C++模板给C++提供了元编程的能力,但大部分用户对 C++ 模板的使用并不 阅读详情

《C++11/14高级编程:Boost程序库探秘》笔记

模板元编程简称元编程,本质上是泛型编程的一个子集,所以从广义上说,所有使用template的泛型编程都可以称作元程序——因为泛型代码并不是真正可编译执行的代码。
模板元编程的运行是在编译期,它把编译器变成了元程序的解释器。

语法元素

模板元编程产生的元程序是在编译期执行的程序,操作的对象也不是普通的变量,因此不能使用运行时的C++关键字(如if、else、for),可用的语法元素相当有限,最常用的几种:

  • enum、static,用来定义编译期的整数常量
  • typedef、using(C++11/14),最重要的元编程关键字,用来定义元数据
  • template,模板元编程的“起点”,主要用于定义元函数
  • “::”,域运算符,用于解析类型作用域获取计算结果(元数据)

元数据

元编程可操作的数据称为“元数据”(meta data),也就是C++编译器在编译期可操作的数据,它是模板元编程的基础。元数据都是不可变的,不能就地修改,最常见的是整数和C++的类型(type)。


元函数

元函数是模板元编程中用于操作处理元数据的“构件”,可以在编译期被“调用”,功能形式类似运行时的函数,但形式上却是一个模板类。

  • 函数参数列表圆括号变成了模板列表声明的尖括号
  • 函数的形参变成了模板参数(即元数据),并且要使用关键字typedname修饰
  • 因为不能使用运行时关键字,所以元函数不能像普通函数那样使用return返回结果,而需要在元函数内部用typedef/using定义一个名为type的类型或名为value的值作为返回
  • 元函数最后以分号结束
template<int N,int M>
struct meta_func
{
    static const int value = N + M;
};

cout << meta_func<10, 10>::value << endl;
int i = 10, j = 10;
meta_func<i, j>::value; // error,i,j为运行时变量

元函数可以使用typedef/using关键字返回多个返回值,并且没有顺序关系,能够用”::”获取。


元函数转发

使用public继承,模板参数传递给父类完成元函数的“调用”,子类会自动获得父类的::type定义,同时也完成了元函数的返回。

struct select1st
{
    typedef T1 type;
};

template<typename T1,typename T2>
struct forward :select1st<T2,T1>    //默认public继承
{

};

等价于:

template<typename T1,typename T2>
struct forward
{
    typedef typename select1st<T2, T1>::type type;  //调用元函数计算
};

mpl工具库

mpl是专门为模板元编程创建的工具库,提供了大量高质量高效的元编程工具,包括:

  • 基本的数据类型:整数、pair、void等运行时类型的对应物
  • 基本的数据运算:以元函数形式提供的算术运算、逻辑运算等运算功能
  • 程序流程控制:以元函数形式提供的分支流程处理功能
  • 容器:模仿STL风格的存储元数据(类型)的编译期容器
  • 视图:容器的适配器,可以简化容器的操作
  • 迭代器:模仿STL风格应用于容器的编译期迭代器
  • 算法:模仿STL风格应用于容器和迭代器的编译期算法
  • 高阶元数据:类似于函数对象的元编程构件
  • bind/lambda表达式:编译期的lambda表达式,功能强大灵活
  • 调试支持:提供了编译期的断言功能
  • 辅助宏:各种配置宏和traits宏

mpl由头文件组成,所有组件位于名字空间boost::mpl,源代码在目录<boost/mpl>下,文件名和组件名一致,不存在统一的头文件,所以要手工添加包含的头文件。


整数类型

虽然整数本身就是元数据,在编译期可以计算,但直接操作整数对于元编程并不太方便,因为元编程更大的作用是类型计算,整数不是类型,为此,mpl库提供了数值包装器概念,把整数(int、long、bool等类型)包装为值元函数。
mpl库里的数值包装器元函数共有六个,分别包装了不同的整数类型(没有short):

char_<N>            :包装char类型,值为N
int_<N>             :包装int类型,值为N
long_<N>            :包装long类型,值为N
size_t<N>           :包装size_t类型,值为N
intergral_c<T,N>    :包装类型为T的整数类型,值为N
bool_<N>            :包装bool类型,值为N

这些元函数都有基本相同的形式,类摘要如下:

struct integral_wrapper //整数包装器
{
    BOOST_STATIC_CONSTANT(T, value = N);    //包装整数值N
    typedef integral_c_tag  tag;            //类型标记
    typedef T               type;           //返回自身
    typedef T               value_type;     //整数类型
    operator T() const { return this->value; }  //转型操作

    //bool没有下列两个元数据
    typedef some_define     next;           //++N
    typedef some_define     prior;          //--N
};

用法也基本相同,用::type返回自身,用::value返回被包含的整数值,对于非bool类型,提供了 next和prior两个内部类型定义,可以获得类似operator++和operator–效果,同时重载了转型操作,使得它们的实例可以在运行时隐式转换为被包装的整数。

#include <type_traits>
#include <boost/mpl/int.hpp>
#include <boost/mpl/integral_c.hpp>
#include <boost/mpl/prior.hpp>
using namespace boost::mpl;

typedef int_<2> i2;
typedef integral_c<short, 2> s2;

int main()
{
    assert(i2::value == 2);
    assert(i2::value == s2::value);

    //is_same<T1,T2>::value比较T1和T2两个的类型是否一致
    assert((std::is_same<i2::type, i2>::value));
    assert((std::is_same<s2::value_type, short>::value));

    assert(i2::next::value == 3);           //内部成员获取递增值
    assert(prior<s2>::type::value == 1);    //使用元函数获取递减值

    i2 two1;    //声明两个包装器实例
    s2 two2;    //值均为常量2

    int i = two1 + two2;        //隐式类型转换为int参与运行时计算
    assert(i == int_<4>());     //与int_元函数比较,使用()创建临时对象
    return 0;
}

mpl库提供了同C++内建的整数运算一样的整数类型运算:

  • 递增递减运算:位于头文件<boost/mpl/next_prior.hpp>,包括next和prior两个元函数,不支持bool_。
  • 算术运算:位于头文件<boost/mpl/arithmetic.hpp>,包括加减乘除、取模、取负值,支持使用多个参数,元函数有plus,minus,negate等。
  • 比较运算:位于头文件<boost/mpl/comparison.hpp>,包括小于,大于,等于和不等于六种,元函数有less,greater,equal_to等。
  • 位运算:位于头文件<boost/mpl/bitwise.hpp>,包括与、或、异或、移位等,元函数有bitand_、bitor_等。
  • 逻辑运算:位于头文件<boost/mpl/logical.hpp>,包括与、或、非三种逻辑运算,元函数有and_、or_和not_。
  • 其他运算:包括最大最小值min/max、整数的大小sizeof_等。

流程控制

mpl提供了四个元函数用于分支结构,类似于运行时的if-else语句

  • if_和if_c
    近似于 ?: 操作符
template<bool C,typename T1,typename T2>
struct if_c
{
    typedef T1 type;
};

template<typename T1,typename T2>
struct if_c<false, T1, T2>          //if_c对false模板偏特化
{
    typedef T2 type;
};

template<typename C,typename T1, typename T2>
struct if_
{
    typedef if_c<C::value, T1, T2> almost_type_t;
    typedef typename almost_type_t::type type;
};

if_c和if_ 的条件不同,前者是bool类型,后者是一个有::value返回的值元函数(不一定是bool_)。

typedef if_c<true, int, long>::type mdata1;
assert((is_same<mdata1, int>::value));

typedef if_<boost::mpl::false_, float, double>::type mdata2;
assert((is_same<mdata2, double>::value));

if_c 和if_ 不能有选择地忽略不需要计算的数据(缓式评估),必须计算所有的元数据,增加了计算时间。

  • eval_if和eval_if_c
    这两者的出现是为了解决上述if_c和if_ 的缺陷:缓式评估问题
template<bool C,typename F1,typename F2>
struct eval_if_c
    : if_c<C,F1,F2>::type   //元函数转发
{};

template<typename C,typename F1, typename F2>
struct eval_if
    : if_<C,F1,F2>::type
{};

使用时需要注意,后两个模板参数必须是元函数。

typedef eval_if_c<true, identity<int>, identity<long>>::type mdata1;
assert((is_same<mdata1, int>::value));

typedef eval_if<is_integral<mdata1>, identity<float>, identity<double>>::type mdata2;
assert((is_same<mdata2, float>::value));

mpl容器

mpl容器与标准库的容器相似,但容纳的元素都是元数据(也就是类型),没有对元素的可拷贝可赋值要求。mpl容器没有成员函数,自身不能操纵容器内元素,只能通过外部元函数处理。
mpl序列容器包括以下内容:

  • list:不同于std::list,它是一个单向链表,只能在序列的前端操作元素可以容纳无限个元素
  • vector:跟std::vector一样有随机访问的功能,但它又有std::list的特性,可以在两端操作元素,容量有限,缺省最多能容纳20个元数据
  • deque:类似std::deque,可以在两端操作元素。

mpl关联容器缺省最多能容纳20个元素,包含以下两种:

  • set:类似std::multiset,是一个允许重复的元数据集合
  • map:类似std::multimap,是一个允许重复的元数据映射关系集合

除了上述五个基本容器,mpl库还针对整数类型提供了一些特别的整数容器:

  • range_c<T,N,M> :不可修改的包含[N,M]区间内整数的vector容器
  • list_c<T,…> :包含类型为T的若干整数的list容器
  • vector_c<T,…> :包含类型为T的若干整数的vector容器
  • set_c<T,…> : 包含类型为T的若干整数的set容器
  • string :专门存储char字符的容器,类似std::string,可以在编译期处理的字符串

判断一个类型是否是mpl容器可以用元函数is_sequence。

mpl容器的相关元函数
1) 容器容量操作。

  • empty<C>
  • size<C>

2)元素访问操作。

  • front<C>
  • back<C>
  • at<C,k> :对于序列容器,返回第k个位置上的元素,对于关联容器,返回键为k的元素
  • at_c<C,n> :仅用于序列容器, 返回第n个位置上的元素(n是long型整数)

3)迭代器操作

  • begin<C>
  • end<C>

4)元素变动操作

  • push_front<C,t>
  • push_back<C,t>
  • pop_front<C>
  • pop_back<C>
  • clear<C>
  • erase<C,pos>/erase<C,f,l>
  • insert<C,pos,t>/insert<C,t>

容器是元数据,元数据是不可变的,所以变动操作都必须返回一个新的容器,原容器不会变化

5)关联容器特有操作

  • has_key<C,k>
  • erase_key<C,k>

mpl迭代器

mpl所有的迭代器都是只读,可分为三类:

  1. 前向迭代器:可以使用元函数next前进
  2. 双向迭代器:在前向迭代器的基础上增加使用元函数prior后退,即可逆向遍历
  3. 随机访问迭代器:在双向迭代器的基础上增加迭代器的距离运算

提供的迭代器元函数包括以下几个:

  • deref<I> : 解引用迭代器,返回元数据
  • next<I> :前进迭代器
  • prior<I> :后退迭代器
  • advance<I,n> :迭代器移动n个位置,对于双向迭代器n可以是负值
  • distance<I1,I2> :返回两个迭代器之间的距离
  • iterator_category<I> :获得迭代器的分类标志

mpl算法

1)插入器

  • back_inserter<C> :在容器后端插入元素,要求支持push_back
  • front_inserter<C> :在容器前端插入元素,要求支持push_front
  • inserter<S,Op> :通用的插入器,以初始状态S开始执行Op操作完成插入动作。
typedef mpl::vector<char,int,long> vec; //容纳三个元素
typedef copy<vec,mpl:back_inserter<vec>>::type vec2; //插入自己,相当于双倍
assert((size<vec2>::value == 6));

typedef mpl::string<> str1;
typedef copy<mpl::string<'time'>, //临时元数据
    mpl::front_inserter<str1>>::type str2;
cout << c_str<str2>::value;     //输出字符串emit

2)查询算法

  • find<C,t> :查找元素t,返回迭代器
  • contains<C,t> :值元函数,检查是否存在元素t
  • count<C,t> :值元函数,返回容器中元素t的个数
  • equal<C1,C2> :值元函数,比较两个容器是否等价,即元数据相同,容器可能是不同类型

3)变换算法
变换算法处理容器中的全部或部分元素,然后返回一个新的容器,带前缀”reverse_”的形式可以返回操作后的逆序容器。

  • copy<From,To> :拷贝一个容器里所有的元素
  • replace<From,Old,New,To> :把容器中的Old元素全部替换为New元素
  • remove<From,t,To> :移除容器中所有值为t的元素
  • reverse<From,To> :逆序拷贝容器里的所有元素

最后一个参数To可以省略不用,直接用::type返回变换后的新容器,如果使用,那么通常需要搭配插入器工作。

4)运行时算法
mpl中for_each算法可以遍历类型容器,工作在运行时,调用一个函数对象操作类型容器里类型对应的实例对象。
for_each算法是一个模板函数,有两个模板参数,第一个参数是mpl容器,必须显式指定,第二个是函数对象,它应具有模板成员函数operator(),能够处理mpl容器中的所有类型,否则编译错误。

struct mpl_func1
{
    template<typename T>
    void operator()(T t)
    {
        cout << typeid(t).name() << endl;
    }
};

struct mpl_func2
{
    template<typename T>
    void operator()(T t)
    {
        if(is_same<typename tag<T>::type,integral_c_tag>::value)
        {
            cout << t << ',';
        }
    }
};

int main()
{
    typedef range_c<int, 0, 5> rc;
    typedef mpl::vector<> vec;

    typedef mpl::copy<rc,
        mpl::back_inserter<vec>>::type vec2;

    typedef push_front<vec2, float>::type vec3;

    mpl::for_each<vec3>(mpl_func1());
    mpl::for_each<vec3>(mpl_func2());
    return 0;
}

运行结果如下:

float
struct boost::mpl::integral_c<int,0>
struct boost::mpl::integral_c<int,1>
struct boost::mpl::integral_c<int,2>
struct boost::mpl::integral_c<int,3>
struct boost::mpl::integral_c<int,4>
0,1,2,3,4,

元编程断言

mpl库在头文件<boost/mpl/assert.hpp>中特意定义了几个元编程断言,能够在元程序发生错误时提供有用的信息。

  • 基本断言
    宏BOOST_MPL_ASSERT是最常用的一个静态断言,它使用一个返回bool_值元函数pred作为参数,断定pred::value为真,调用形式:
    BOOST_MPL_ASSERT((pred)); //必须两对圆括号
typedef mpl::vector<int,char> vec;
BOOST_MPL_ASSERT((is_same<int,front<vec>::type>));  //注意不需要::value
BOOST_MPL_ASSERT((equal_to<size<vec>::type,int_<3>>));

第二个断言会在编译时报出形如”***pred::***”的错误,同时出现行号。

  • 否定断言
    BOOST_MPL_ASSERT_NOT
  • 关系断言
    BOOST_MPL_ASSERT_RELATION(x,rel,y)
    x,y是两个编译期整数(非包装器),rel是一个合法的C++关系操作符,如==、<。
    这个不需要两对圆括号
  • 定制消息断言
    BOOST_MPL_ASSERT_MSG(c,msg,types_)
    c:条件表达式,非元函数
    msg:自定义的消息,但不是一个C字符串,而是一个符合C++语法的标志符
    types_:类型列表

实例研究

实现一个动态加载动态链接库函数功能的实例研究
先编译一个简单的动态库:

//libtest.h
#ifdef __cplusplus
extern "C"
{
#endif
    int so_func1(int x);
    int so_func2(int x,int y);
#ifdef __cplusplus
}
#endif
//libtest.cpp
#include "libtest.h"
int so_func1(int x)
{
    return x*x;
}
int so_func2(int x,int y)
{
    return x + y;
}

//g++-4.8 libtest.cpp -fPIC -shared -o libtest.so
1.泛型编程版本

简单地封装了一个so操作类

//gp_version.cpp
#include <iostream>
#include <stdexcept>    //runtime_error
#include <dlfcn.h>      //UNIX动态链接头文件
#include "libtest.h"

class DlManager         //定义一个加载so函数的包装类
{
public:
    DlManager(const char* name) 
    {
        m_h = dlopen(name,RTLD_NOW);    //使用文件名加载so
    }
    ~DlManager()
    {
        dlclose(m_h);
    }

    template<typename FuncType>
    FuncType load(const char* func_name)
    {
        FuncType pf = reinterpret_cast<FuncType>(dlsym(m_h,func_name)); //强制类型转换

        if(!pf)
        {
            throw std::runtime_error(dlerror());
        }

        return pf;
    }
private:
    typedef void* handle_t;     //句柄类型定义
    handle_t m_h;               //so文件的句柄
};

typedef int (*Func1)(int);
typedef int (*Func2)(int,int);

int main()
{
    DlManager dm("./libtest.so");

    Func1 f1 = dm.load<Func1>("so_func1");
    Func2 f2 = dm.load<Func2>("so_func2");

    std::cout << f1(10) << std::endl;
    std::cout << f2(10,20) << std::endl;

    return 0;
}

//g++-4.8 gp_version.cpp -ldl -o gp_version
2.元编程第1版

加载动态库这个过程,可以分解成两部分:一部分是编译期的数据,包括文件名、函数名以及函数指针类型,另一部分是运行时代码,包括加载so文件和获取so函数,泛型版本没有把这两者很好地解耦,而是将两者在运行时混合在一起了。
为了明确区分编译和运行时的数据,使用mpl把程序分为前端和后端两个部分,前端使用mpl定义编译期数据,后端使用前端数据实现运行时功能。

前端
前端数据有so文件名、接口函数名和函数指针类型,前两者可以使用mpl::string表述,后者本身就是元数据,为了实现函数名与函数指针类型的对应关系,可以使用mpl::map,最后用struct把这些数据封装成一个前端类。

后端
后端与泛型版本差不多,只不过操作的数据变成了前端的元数据,把后端实现为一个模板类,这样就可以使用不同的前端类支持不同的so加载功能。

//mp_version1.cpp
#include <iostream>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/string.hpp>
#include <boost/mpl/map.hpp>
#include <boost/mpl/at.hpp>
#include <dlfcn.h>      //UNIX动态链接头文件
#include "libtest.h"
namespace mpl = boost::mpl;

struct dl_front
{
    typedef mpl::string<'./','lib','test','.so'> so_name;

    typedef int (*Func1)(int);
    typedef int (*Func2)(int,int);

    typedef mpl::string<'so_','func','1'> func1_name;
    typedef mpl::string<'so_','func','2'> func2_name;

    typedef mpl::map<
        mpl::pair<func1_name,Func1>,
        mpl::pair<func2_name,Func2>
        > map_fun;
};

template<typename Front>
class dl_back
{
private:
    void* m_h = NULL;
public:
    dl_back()
    {
        m_h = dlopen(mpl::c_str<typename Front::so_name>::value,RTLD_NOW);
    }
    ~dl_back()
    {
        dlclose(m_h);
    }

    template<typename FuncName>
    typename mpl::at<typename Front::map_fun,FuncName>::type    //返回类型
    func_ptr()
    {
        typedef typename mpl::at<typename Front::map_fun,FuncName>::type result_type;
        result_type pf = reinterpret_cast<result_type>(dlsym(m_h,mpl::c_str<FuncName>::value));

        return pf;
    }
};

int main()
{
    dl_back<dl_front> dl;
    std::cout << dl.func_ptr<dl_front::func1_name>()(10) << std::endl;
    std::cout << dl.func_ptr<dl_front::func2_name>()(10,20) << std::endl;

    return 0;
}

//g++-4.8 mp_version1.cpp -ldl -o mp_version1

在调用成员函数func_ptr()时,除了显式写出前端定义的函数名外,还必须调用两次operator(),因为第一次调用只是获取了函数指针,第二次调用材质真正的so接口函数调用。

3.元编程第2版

第一版的元编程还可以增强成员函数func(),直接传递参数减少一次operator()调用。要减少一个operator()调用,就要求函数func()返回的是so函数指针的返回类型而不是函数指针类型,同时传递相应数量的参数,前者可以使用result_of结合mpl来自动推导函数的返回值类型,后者可以用可变模板参数列表解决。
改进后func()代码:

template<typename FuncName,typename ... Args>   //可变参数模板
    typename boost::result_of<                      //使用result_of推导函数的返回值
        typename mpl::at<typename Front::map_fun,FuncName>::type(Args...)
        >::type
    func(Args const& ...args)
    {
        typedef typename mpl::at<typename Front::map_fun,FuncName>::type func_type; //得到函数指针类型
        func_type pf = reinterpret_cast<func_type>(dlsym(m_h,mpl::c_str<FuncName>::value)); //获取函数指针

        return pf(args...); //直接调用函数指针
    }

也可以直接使用第一版写好的func_ptr()函数直接返回函数指针来调用:

template<typename FuncName,typename ... Args>   //可变参数模板
    typename boost::function_traits<            //改用function_traits推导类型
        typename boost::remove_pointer<         //需要先移除类型里的指针
            typename mpl::at<typename Front::map_fun,FuncName>::type
            >::type             
        >::result_type                  //获取函数的返回值
    func_V(Args const& ... args)
    {
        return func_ptr<FuncName>()(args...);
    }

完整代码:

//mp_version2.cpp
#include <iostream>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/string.hpp>
#include <boost/mpl/map.hpp>
#include <boost/mpl/at.hpp>
#include <boost/utility/result_of.hpp>
#include <boost/type_traits.hpp>
#include <dlfcn.h>      //UNIX动态链接头文件
#include "libtest.h"
namespace mpl = boost::mpl;

struct dl_front
{
    typedef mpl::string<'./','lib','test','.so'> so_name;

    typedef int (*Func1)(int);
    typedef int (*Func2)(int,int);

    typedef mpl::string<'so_','func','1'> func1_name;
    typedef mpl::string<'so_','func','2'> func2_name;

    typedef mpl::map<
        mpl::pair<func1_name,Func1>,
        mpl::pair<func2_name,Func2>
        > map_fun;
};

template<typename Front>
class dl_back
{
private:
    void* m_h = NULL;
public:
    dl_back()
    {
        m_h = dlopen(mpl::c_str<typename Front::so_name>::value,RTLD_NOW);
    }
    ~dl_back()
    {
        dlclose(m_h);
    }

    template<typename FuncName>
    typename mpl::at<typename Front::map_fun,FuncName>::type    //返回类型
    func_ptr()
    {
        typedef typename mpl::at<typename Front::map_fun,FuncName>::type result_type;
        result_type pf = reinterpret_cast<result_type>(dlsym(m_h,mpl::c_str<FuncName>::value));

        return pf;
    }

    template<typename FuncName,typename ... Args>   //可变参数模板
    typename boost::result_of<typename mpl::at<typename Front::map_fun,FuncName>::type(Args...)>::type  //使用result_of推导函数的返回值
    func(Args const& ... args)
    {
        typedef typename mpl::at<typename Front::map_fun,FuncName>::type func_type; //得到函数指针类型
        func_type pf = reinterpret_cast<func_type>(dlsym(m_h,mpl::c_str<FuncName>::value)); //获取函数指针

        return pf(args...); //直接调用函数指针
    }

    template<typename FuncName,typename ... Args>   //可变参数模板
    typename boost::function_traits<typename boost::remove_pointer<typename mpl::at<typename Front::map_fun,FuncName>::type>::type>::result_type                //获取函数的返回值 //改用function_traits推导类型
    func_V(Args const& ... args)
    {
        return func_ptr<FuncName>()(args...);
    }

};

int main()
{
    dl_back<dl_front> dl;
    std::cout << "----------------mp version 2-----------" << std::endl;
    std::cout << dl.func<dl_front::func1_name>(10) << std::endl;
    std::cout << dl.func<dl_front::func2_name>(10,20) << std::endl;

    std::cout << "----------------mp version 2-----------" << std::endl;
    std::cout << dl.func_V<dl_front::func1_name>(10) << std::endl;
    std::cout << dl.func_V<dl_front::func2_name>(10,20) << std::endl;

    return 0;
}

//g++-4.8 mp_version2.cpp -ldl -o mp_version2
C++模板元编程详细教程(之一) 《C++模板元编程详细教程》系列文章的第一篇,主要介绍了模板的概念以及基本的模板语法和相关示例。 阅读详情

相关推荐

C++模板元编程详解

C++模板是支持泛型编程的重要特性,允许编写与类型无关的代码,提升代码复用性和灵活性。模板分为函数模板和类模板,分别用于生成通用函数和类。模板参数包括类型参数、非类型参数和模板模板参数。模板实例化是编译器根据具体类型生成代码的过程,而模板特化则允许为特定类型提供特殊实现。模板元编程(TMP)利用模板在编译时进行计算或生成代码,结合constexpr、SFINAE、类型萃取、可变参数模板等技术,实现编译期计算和类型操作。

weixin_43484713的博客 1844

C++ 模板元编程简介

1.概述 模板元编程(Template Metaprogramming,TMP)是编写生成或操纵程序的程序,也是一种复杂且功能强大的编程范式(Programming Paradigm)。C++模板给C++提供了元编程的能力,但大部分用户对 C++ 模板的使用并不是很频繁,大致限于泛型编程,在一些系统级的代码,尤其是对通用性、性能要求极高的基础库(如 STL、Boost)几乎不可避免在大量地使用 C...

Doublelv 的博客专栏。 4748

C++模板元编程详细教程(之十一)

《C++模板元编程详细教程》系列的第11篇,也是完结篇,主要介绍了visit的实现方法、C++的模板范式,以及一些Q&A和感悟

fl2011sx的博客 3675

模板元编程(TMP)

元编程是什么 元編程(英語:Metaprogramming),又称超编程,是指某类计算机程序的编写,这类计算机程序编写或者操纵其它程序(或者自身)作为它们的资料,或者在运行时完成部分本应在编译时完成的工作。多数情况下,与手工编写全部代码相比,程序员可以获得更高的工作效率,或者给与程序更大的灵活度去处理新的情形而无需重新编译。 泛泛来说,可扩展程序自身,这样的能力,为元编程。——比如,若编程甲可...

Jeff_的博客 2864

C++模板元编程详细教程(之五)

《C++模板元编程详细教程》系列文章的第5篇,主要介绍了模板元编程两大要素的第一要素——静态数值计算

fl2011sx的博客 4552

C++ 模板元编程深度剖析

模板元编程(TMP)是一种编程技术,使用 C++ 模板在编译时进行计算和决策,而不是在运行时。它的基本思想是通过模板机制(如模板特化、递归模板、constexpr等)将计算推迟到编译阶段,从而优化程序的性能。模板元编程在 C++ 中是一种非常强大的技术,它使得程序能够在编译时进行复杂的计算和优化,从而显著提升运行时的效率和灵活性。模板元编程的核心在于递归模板、模板特化、constexpr和 SFINAE 等机制,通过合理运用这些特性,可以使得 C++ 代码更具表现力和可维护性。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 1282

C++模板元编程

所谓的元编程就是编写直接生成或操纵程序的程序,C++模板为C++语言提供了元编程的能力。模板使 C++ 编程变得异常灵活,能实现很多高级动态语言才有的特性(语法上可能比较丑陋,一些历史原因见下文)。普通用户对 C++ 模板的使用可能不是很频繁,大致限于泛型编程,但一些系统级的代码,尤其是对通用性、性能要求极高的基础库(如 STL、Boost)几乎不可避免的都大量地使用 C++ 模板,一个稍有规模的大量使用模板的程序,不可避免的要涉及元编程(如类型计算)。本文就是要剖析 C++ 模板元编程的机制。

WHEgqing的专栏 1万+

C++模板元编程(C++ template metaprogramming)

本文就是要剖析 C++ 模板元编程的机制。C++ 模板是图灵完备的,这使得 C++ 成为两层次语言(two-level languages,中文暂且这么翻译,文献[9]),其中,执行编译计算的代码称为静态代码(static code),执行运行期计算的代码称为动态代码(dynamic code),C++ 的静态代码由模板实现(预处理的宏也算是能进行部分静态计算吧,也就是能进行部分元编程,称为宏元编程,见 Boost 元编程库即 BCCL,文献[16]和文献[1] 10.4)。

2401_86940371的博客 1796

全面理解-模板元编程

它通过模板特化、递归实例化和类型操作,在编译时完成传统运行时才能处理的任务,从而实现零运行时开销的优化。是 C++ 中强大的编译期计算工具,依赖模板特化、递归和类型操作。:通过模板参数推导和类型萃取(Type Traits)操作类型。通过递归模板实例化实现数值计算(如阶乘、斐波那契数列)。:所有计算在编译阶段完成,生成高效代码。:结果在编译期确定,不增加程序运行负担。:编译期计算、类型萃取、条件编译优化。:递归实例化和模板展开增加编译时间。:编译期类型检查避免运行时错误。通过类型萃取和操作生成新类型。

HL_LOVE_C的博客 2022

Tesseral 建立地震模型

0. 前言 本节内容主要是总结如何使用 Tesseral 工具建立模型。 1. 模型建立步骤 设置模型的宽度和深度 此处默认都为1000,之后点击“确定” 建立地层 绘制新的地层 右键点击“New polygon” 绘制地层,设置第二个层的名字和纵波速度 绘制第三层,第三层覆盖在前一个层上 第四层如上,第四层如果是最后一层,绘制该层时,要将深度1000包围。 设置观测系统 设置炮点 设置子波为雷克子波,频率设置为30 检波点设置 运行模型 设置好如上选择后,

weixin_41857483的博客 6539

STM32学习笔记(江协科技)-----OLED显示屏

SSD1306简介:•SSD1306是一款OLED/PLED点阵显示屏的控制器,可以嵌入在屏幕中,用于执行接收数据、显示存储、扫描刷新等任务••驱动接口:128个SEG引脚和64个COM引脚,对应128*64像素点阵显示屏••内置显示存储器(GDDRAM):128*64 bit128*8 ByteSRAM••供电:IC逻辑),VCC=7~15V(面板驱动)••通信接口:8位6800/8080并行接口,3/4线SPI。

lu322f的博客 1062

C++模板元编程简介

1.概述 模板元编程(Template Metaprogramming,TMP)是编写生成或操纵程序的程序,也是一种复杂且功能强大的编程范式(Programming Paradigm)。C++模板给C++提供了元编程的能力,但大部分用户对C++模板的使用并不是很频繁,大致限于泛型编程,在一些系统级的代码,尤其是对通用性、性能要求极高的基础库(如 STL、Boost)几乎不可避免在大量地使用 C++ 模板以及模板元编程。 模版元编程完全不同于普通的运行期程序,因为模版元程序的执行完全是在编译期,并且模版

sunweiliang的博客 1633

基于STM32+4G模块设计的智能灌溉系统

当前智能灌溉系统分为三个部分:温湿度采集部分、灌溉系统部分、OneNet云平台。 灌溉系统部分增加刷卡功能,用户可以通过IC卡刷卡将金额刷到灌溉系统设备里,灌溉系统设备里会记录金额,并且在手机可视化页面上会显示当前的设备金额。 金额会随着时间进行递减,按分钟计费,如果计费到0,就无法启动灌溉,需要重新充值金额才能继续。

4514

C++ 高阶操作:模板元编程

星标/置顶公众号????,硬核文章第一时间送达!链接 | https://www.cnblogs.com/xiangtingshen/p/11613183.html泛型编程大家应该都很熟悉了,...

青春不老,奋斗不止! 2200

【人脸识别/轻量级模型】MobileFaceNet:适用于人脸识别的轻量级网络模型(文末附pytorch代码)

MobileFaceNet:用于人脸识别的轻量级网络模型

Roaddd的博客 6988

GEPA_AIME教程:使用GEPA优化数学问题求解的完整指南

本教程展示了如何使用DSPy框架中的GEPA(Generative Evolutionary Prompt Adaptation)优化器来提升大语言模型在数学竞赛问题(AIME)上的表现。# 创建基础的链式思考程序"""带有详细反馈的评估指标,为GEPA提供学习信号"""try:# 处理解析错误feedback_text = f"最终答案必须是一个有效的整数。你回答了'

步子哥的博客 1386

C++中的模板元编程是什么?

而模板元编程(Template Metaprogramming,简称TMP)则是C++模板功能的一种高级应用,它允许在编译时进行复杂的类型计算和代码生成。C++中的模板元编程是一种强大的编程技术,它允许程序员在编译时进行复杂的类型计算和代码生成。模板元编程起源于C++标准模板库(STL)的设计过程中,当时的设计者们发现,通过模板的嵌套和递归调用,可以在编译时执行一些复杂的计算和操作。:通过模板元编程,我们可以在编译时进行复杂的计算和代码生成,从而减少运行时的计算量,提高程序的性能。

Colbyacon的博客 1045

华为OD机试真题 新系统 2026-04-19 Java&Go 实现【分辨率排序】

华为OD机试真题 新系统 2026-04-19 Python&JS 实现【分辨率排序】

MISAYAONE的博客 306

(二)模板元编程简介

元编程(meta-programming)也被称为“超程序”,“超编程”或“产生式编程”,这样说法一定程度上反映了其本质——它是一种位于普通程序之上、超越普通程序的程序,可以操纵、产生程序的程序。模板元编程本质上是泛型编程的一个子集,从广义上来说,所有使用template 的泛型代码都可以称作元程序——因为泛型编程代码并不是真正可编译执行的代码,它们只是定义了代码的产生规则,是用来生成代码的“模板”。然而模板元编程又不完全等同于泛型编程,它是一种“函数式编程”,是图灵完备的,可以“计算”任何东西。

icc_hhy的博客 1827

Ambari 3.0+BigTop 3.3集群部署实战

Ambari 3.0+BigTop 3.3集群部署

docsz的博客 609

15、分布式查询处理详解

本博文深入探讨了分布式环境下的查询处理与优化技术。从查询处理的基本概念出发,分析了在分布式系统中执行查询所面临的额外挑战,并通过实例比较了不同的执行策略。文章详细介绍了查询优化的组成要素,包括搜索空间、成本模型和搜索策略,同时涵盖了查询处理的多个层次以及数据本地化的核心方法。此外,还讨论了连接排序优化、分布式成本模型以及多种查询优化方法,如静态优化、动态优化和混合优化。最后,提出了自适应查询处理的概念,旨在提高分布式查询的灵活性和效率。通过综合应用这些技术,可以有效提升分布式数据库的查询性能,降低通信和计算

gan8painter的博客 73

C++模板元编程:编译时的魔法

模板允许我们定义可以处理多种数据类型的函数或类。// 打印整数// 打印浮点数// 打印字符串return 0;

无远弗届 1098

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

类的模板元编程:C++中的模板元编程技巧

1.背景介绍 在C++中,模板元编程(Template Metaprogramming)是一种编程技巧,它允许程序员在编译期间,而不是运行时间,完成一些复杂的计算和数据操作。这种技术可以用来优化程序的性能,提高代码的可读性和可维护性。 在本文中,我们将讨论模板元编程的背景、核心概念、算法原理、最佳实践、应用场景、工具和资源推荐,以及未来的发展趋势和挑战。 1. 背景介绍 模板元编程起源于1...

AI天才研究院 1118

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

C++ 模板元编程的应用有哪些,意义是什么?

https://www.cnblogs.com/liangliangh/p/4219879.html 为了谈应用,先谈谈使命。模板元编程的根在模板。模板的使命很简单:为自动代码生成提供方便。提高程序员生产率的一个非常有效的方法就是“代码复用”,而面向对象很重要的一个贡献就是通过内部紧耦合和外部松耦合将“思想”转化成一个一个容易复用的“概念”。但是面向对象提供的工具箱里面所包含的继承,组合与多态并...

南方以北 2492

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

上一篇: C++新语言
下一篇: 预处理元编程
ProHayley
博客等级 码龄14年 49粉丝 48原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值