函数模板“偏特化” (C++)

时间:2022-09-17 08:49:41
     模板是C++中很重要的一个特性,利用模板可以编写出类型无关的通用代码,极大的减少了代码量,提升工作效率。C++中包含类模板、函数模板,对于需要特殊处理的类型,可以通过特化的方式来实现特定类型的特殊操作。
 
     最近工作中,需要处理CONT<TYPE>这种复合类型和T这种自定义类型的模板特化,因为CONT类型有五种左右需要特殊处理,其余的可以用默认处理函数,TYPE的具体类型有上千种,但是TYPE类型不涉及不同的操作。
     方案一:采用两个模板参数
template <typename TYPE, template<typename> typename CONT>
void func(const CONT<TYPE> &v) {}
//或
template <typename T>
void func(const T &v) {}
     问题:由于C++不支持模板函数偏特化,实际特化的时候会退化的第二个的情形,即需要手动编写几千个函数来实现特化,这种方法不可接受。
 
     方案二:采用偏特化类模板的第二个模板参数
template<typename TYPE, template<typename> typename CONT>
struct my_class {
my_class() = delete;
static void func(const CONT<TYPE> &v) {}
};
//将CONT偏特化为vector
template <typename TYPE>
struct my_class<TYPE, vector> {
my_class() = delete;
static void func(const vector<TYPE> &v) {}
};
     问题:首先,这种形式的偏特化可以实现复合类型的需求,但是对于用户自定义类型无法兼容
 
     方案三:利用SFINAE控制模板函数实例化实现“偏特化” (示例代码部分使用C++17语法)
//“偏特化”vector容器类型的模板函数
template <typename TYPE, template<typename> typename CONT>
void func(const CONT<TYPE> &v, std::enable_if_t<std::is_fundamental<TYPE>::value && std::is_same_v<vector<int>, CONT<int>>> * = nullptr)
{} //如果使用C++11版本的gcc时,偏特化vector等标准容器类型的模板函数需要使用如下形式
template <typename T, template<class, class...> class C, class... Args>
void var_func(const C<T, Args...>& v)
{
  std::cout << "var_func type" << std::endl;
}
//“偏特化”用户自定义非容器类型的函数
template <typename TYPE, template<typename> typename CONT>
void func(const TYPE &v, std::enable_if_t<!std::is_fundamental<TYPE>::value> * = nullptr)
{} //默认的复合类型模板函数
template <typename TYPE, template<typename> typename CONT>
void func(const CONT<TYPE> &v, std::enable_if_t<std::is_fundamental<TYPE>::value
&& !std::is_same_v<vector<int>, CONT<int>>
&& !std::is_same_v<deque<int>, CONT<int>>
&& !std::is_same_v<list<int>, CONT<int>>> * = nullptr)
{} //对于函数体内的auto类型的变量(由CONT和TYPE组合成的复合类型),可以通过以下手段判断其类型
using T = std::decay_t<decltype(variable)>;
if constexpr (std::is_same_v<vector<type>, T>)
func<type, vector>(variable);
else if constexpr (std::is_same_v<my_type, T>)
func<my_type, deque>(variable);
....
     结论:最后采用了这种方案,结合了std::enable_if_t, std::is_same_v, std::is_base_of 等语法来实现模板函数的条件来实现了函数模板的“偏特化”
     
Tips:
  1. 函数模板的定义和声明必须都放到头文件中,因为编译器在编译阶段需要依据头文件中模板函数的定义来实现模板的实例化
  2. 不建议将函数模板编译成静(动)态库,然后源文件只包含模板函数声明的头文件,这样在链接的时候提示undefined 的类似错误,因为函数库只能导出实例化的函数,对于没有实例化模板函数,就会出现undefined的错误(除非在函数库中,将所有可能的实例化类型都实例化一遍)。建议,对模板的使用通过头文件的方式。
  3. 类模板如果有默认模板参数,那么只能在声明或者定义其中任何一个位置设置默认模板参数,不能两个位置都设置。

函数模板“偏特化” (C++)的更多相关文章

  1. C&plus;&plus; 函数模板&OpenCurlyDoubleQuote;偏特化”

         模板是C++中很重要的一个特性,利用模板可以编写出类型无关的通用代码,极大的减少了代码量,提升工作效率.C++中包含类模板.函数模板,对于需要特殊处理的类型,可以通过特化的方式来实现特定类型 ...

  2. &lbrack;转&rsqb;Traits 编程技法&plus;模板偏特化&plus;template参数推导&plus;内嵌型别编程技巧

    STL中,traits编程技法得到了很大的应用,了解这个,才能一窥STL奥妙所在. 先将自己所理解的记录如下: Traits技术可以用来获得一个 类型 的相关信息的. 首先假如有以下一个泛型的迭代器类 ...

  3. 【转】traits技术及模板偏特化

    #include <iostream> using namespace std; struct __xtrue_type { }; // define two mark-type stru ...

  4. 【校招面试 之 C&sol;C&plus;&plus;】第2题 函数模板、类模板、特化、偏特化

    1.C++模板 说到C++模板特化与偏特化,就不得不简要的先说说C++中的模板.我们都知道,强类型的程序设计迫使我们为逻辑结构相同而具体数据类型不同的对象编写模式一致的代码,而无法抽取其中的共性,这样 ...

  5. C&plus;&plus;的模板特化 和 STL中iterator&lowbar;traits模板的偏特化

    C++中有类模板和函数模板,它们的定义如下所示: 类模板: template<class T1,class T2> class C { //... }; 函数模板: template&lt ...

  6. c&plus;&plus;模板特化偏特化

    模板为什么要特化,因为编译器认为,对于特定的类型,如果你对某一功能有更好地实现,那么就该听你的. 模板分为类模板与函数模板,特化分为全特化与偏特化.全特化就是限定死模板实现的具体类型,偏特化就是模板如 ...

  7. C&plus;&plus;中模板的特化与偏特化

    1.引言 C++中的模板分为类模板和函数模板,虽然它引进到C++标准中的时间不是很长,但是却得到了广泛的应用,这一点在STL中有着充分的体现.目前,STL在C++社区中得到了广泛的关注.应用和研究.理 ...

  8. &lbrack;转&rsqb;C&plus;&plus;中模板的特化与偏特化

    转载自:http://hi.baidu.com/klcdyx2008/blog/item/5adbf77b79f316f90bd1873c.html 1.引言C++中的模板分为类模板和函数模板,虽然它 ...

  9. C&plus;&plus; 模板的全特化与偏特化

    模板为什么要特化,因为编译器认为,对于特定的类型,如果你能对某一功能更好的实现,那么就该听你的. 模板分为类模板与函数模板,特化分为全特化与偏特化.全特化就是限定死模板实现的具体类型,偏特化就是如果这 ...

随机推荐

  1. Android原生json和fastjson的简单使用

    android原生操作json数据 主要是两个类 JSONObject 操作对象     JONSArray操作json数组 对象转json //创建学生对象 Student student=new ...

  2. BackgroundWorker控件

    在我们的程序中,经常会有一些耗时较长的运算,为了保证用户体验,不引起界面不响应,我们一般会采用多线程操作,让耗时操作在后台完成,完成后再进行处理或给出提示,在运行中,也会时时去刷新界面上的进度条等显示 ...

  3. IOS 杂笔-5(NSTimer极浅析)

    1.timer都会对它的target进行retain,我们需要小心对待这个target的生命周期问题,尤其是重复性的timer. 2. timer不是一种实时的机制,会存在延迟,而且延迟的程度跟当前线 ...

  4. mysql笔记01 MySQL架构与历史、Schema与数据类型优化

    MySQL架构与历史 1. MySQL架构推荐参考:http://www.cnblogs.com/baochuan/archive/2012/03/15/2397536.html 2. MySQL会解 ...

  5. Java开发环境的基本设置

    作为Java的刚開始学习的人,不知道其它的刚開始学习的人有没有和我一样的感受:用Java开发须要配置这么复杂 的环境.太难了.第一次配置时,一团混乱.Oracle监听服务打不开了,PLSql连接不上O ...

  6. 1592&colon; &lbrack;Usaco2008 Feb&rsqb;Making the Grade 路面修整

    1592: [Usaco2008 Feb]Making the Grade 路面修整 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 428  Solv ...

  7. 【转】三个案例带你看懂LayoutInflater中inflate方法两个参数和三个参数的区别

    关于inflate参数问题,我想很多人多多少少都了解一点,网上也有很多关于这方面介绍的文章,但是枯燥的理论或者翻译让很多小伙伴看完之后还是一脸懵逼,so,我今天想通过三个案例来让小伙伴彻底的搞清楚这个 ...

  8. 第一次远程ubuntu用c写Hello Word出现的问题

    2019-03-26 21:48:48 之前已经学过c语言,但是一直是在win10上用VC++6.0来写代码的,现在想尝试在linux下用c语言 其实主要目的是来学习linux 1.问题: 在ubun ...

  9. python 3 字符编码解码问题

    python2与python3 字符编码都做了很大的调整,区别: 1.python2字符串默认有两种类型,unicode和str.'你好' !=u'你好' python3字符串默认只有str一种类型, ...

  10. 生成器函数&lowbar;yield&lowbar;yield from&lowbar;send

    生成器函数 ''' yield类似于return 相同: 都返回出去一个值 不同: yield每次返回时, 会记录当前执行的位置 等下次调用生成器, 会从该位置向下走 return 直接终止函数 '' ...