本文转自:https://www.cnblogs.com/dracohan/p/3401660.html 转来收藏以便查阅,感谢原作者
今天在写代码时,遇到了模板和特化,在网上找了资料后问题呗一一解决,转载此文用于以后查阅,感谢原创者。其中增加了我自己的总结:
特化函数与模板函数的区别:
(1)、模板函数的T参数只能传入类类型的参数;特化函数的参数只能传入对应的参数类型,基本类型或类类型。
(2)、模板函数在link时检查类型,编译时只是当成了注释;特化函数及参数在编译时检查是否匹配或有错。
(3)、特化函数与模板函数重名时,特化函数需要加特殊处理。一种方式是加inline修饰,不在编译时生成对应的符号表。
我正在用一个基于模板的库源代码,该库包含一些针对特定类型的模板函数特化。类模板,函数模板和模板函数特化都在头文件中。我在我的.cpp文件中 #include 头文件并编译链接工程。但是为了在整个工程中使用该库,我将头文件包含在 stdafx.h 中,结果出现特化模板函数的符号多重定义错误。我要如何组织头文件才能避免多重符号定义错误?我用 /FORCE:MULTIPLE,但我想用一个更好的解决方法。
Lee Kyung Jun
实际上,确实用更好的解决方法。稍后我会解释,但首先让我重温一下模板函数特化是如何工作的。假设你有一个比较两个基于 operator> 和 operator== 对象的模板函数:
template <typename T>
int compare(T t1, T t2)
{
return t1==t2 ? 0 : t1 > t2 ? 1 : -1;
}
该模板根据地一个参数是否等于、大于、或小于第二个参数而分别返回零或+/-1。它是典型的用于集合排序时的排序函数。它假设类型 T 具备 operator== 和 operator> 操作,并支持 int,float,double 或 DWORD 类型。但它不能应用于比较自负串(char* 指针),因为这个函数比较的是串指针,而不是字符串本身:
LPCTSTR s1,s2;
...
int cmp = compare(s1,s2); // s1<s2? Oops!
为了能进行字符串比较,你需要一个使用 strcmp 或其 TCHAR 版本 _tcscmp 的模板特化:
// specialization for strings
template<>
int compare<LPCTSTR>(LPCTSTR s1, LPCTSTR s2)
{
return _tcscmp(s1, s2);
}
没错,这样做完全正确,现在的问题是:将这个特化放在何处?显然是要放在模板的头文件中。但这样会导致符号多重定义的错误,就像 Lee 遇到的那样。原因很明显,模板特化是一个函数,而非模板。它与下面的写法是一样的:
int compare(LPCTSTR s1, LPCTSTR s2)
{
return _tcscmp(s1, s2);
}
没有理由不在头文件中定义函数——但是一旦这样做了,那么你便无法在多个文件中 #include 该头文件。至少,肯定会有链接错误。怎么办呢?
如果你掌握了模板函数特化即函数,而非模板的概念,你就会认识到有三个选项,完全与普通函数一样;特化为 inline,extern 或者 static。例如,像下面这样:
template<>
inline int compare<LPCTSTR>(LPCTSTR s1, LPCTSTR s2)
{
return _tcscmp(s1, s2);
}
对于大多数模板库而言,这是最容易和最常见的解决方案。因为编译器直接扩展内联函数,不产生外部符号,在多个模块中 #include 它们没有什么问题。链接器不会出错,因为不存在多重定义的符号。对于像 compare 这样的小函数来说,inline 怎么说都是你想要的(它更快)。
但是,如果你的特化很长,或出于某种原因,你不想让它成为 inline,那要如何做呢?此时可以做成 extern。语法与常规函数一样:
// in .h header file
template<>
extern int compare<LPCTSTR>(LPCTSTR s1, LPCTSTR s2);
当然,你得在某个地方实现 compare。部分细节如 Figure 7 所示。我在单独的模块 Templ.cpp 中实现了特化,它与主工程链接。Templ.h 被 #include 在 stdafx.h 中,而 stdafx.h 又被 #include 在 Templ.cpp 和主模块两个文件中——生成工程没有链接错误。去下载源代码自己尝试一下吧。
如果你正在为其他开发人员写模板库,extern 方式会很不爽,因为你必须创建一个带目标模块的链接库(lib),它包含有特化。如果你已经有了一个这样的 .lib,也没什么;如果没有,你可能会想方设法避免引入这样的库。仅用头文件实现模板是更好的方法(麻烦少)。最容易的方式是用 inline,此外,你还能将你的特化放在单独的头文件中,使之与其声明分开并要其他开发人员只在一个模块中 #include 特化。还有一个可选的方法是将所有东西放在一个文件中,并用预处理符号控制实例化:
#ifdef MYLIB_IMPLEMENT_FUNCS
template<>
int compare<LPCTSTR>(LPCTSTR s1, LPCTSTR s2)
{
return _tcscmp(s1, s2);
}
#endif
使用该方法,所有模块都包含此头文件,但在包含它之前,只有一个 #define MYLIB_IMPLEMENT_FUNCS。这个方法不支持预编译头,因为编译器用 stdafx.h 中的任何 MYLIB_IMPLEMENT_FUNCS 值加载预编译版本。
避免符号多重定义错误的最后同时也是用得最少的一个方法是将特化做成 static:
template<>
static int compare<LPCTSTR>(LPCTSTR s1, LPCTSTR s2)
{
return _tcscmp(s1, s2);
}
这样链接器也不会出错,因为静态函数不向外界输出其函数,并且它让你将所有东西都保持在一个头文件中,不用引入预处理符号。但它缺乏效率,因为每个模块都有一个函数拷贝。如果函数小到没什么——那为何不用内联呢?
所以简言之:将特化做成 inline 或 extern。通常都是用 inline。两种方法都得编辑头文件。如果使用的是第三方的库没有头文件,那么你除了用链接选项 /FORCE:MULTIPLE 之外别无选择。在你等着生成你的工程时,你可以告诉编写库文件的那个家伙——为什么要将函数模板特化定义成 inline 或者 extern。就说是我说的。
------------
typename名字能更清楚的表明后面的名字是类型名,但是关键字typename是最近加入到标准C++中 (16)编译器如何分析模板定义:(编译时刻分析模板定义(注:不是模板实例化)) (17)模板类型参数: 函数定义或声明跟在模板参数表 (21)模板参数表: (22)函数参数表: (23)模板实参推演:(函数的返回值类型能推演否?) 当函数模板被调用时,对函数实参类型的检查决定了模板实参的类型和值.这个过程被 (24)显式地指定模板实参 但是当模板实参被显式指定时就没有必要推演模板实参了. 一方面如果我们指定了显式模板参数则必须检查显式模板实参对于函数实参的新类型是 1.我们也可以声明一个函数模板的显式特化而不定义 (31)特化整个类模板 |
PG12.9-Repmgr5.1.0-vip-3节点部署Centos 7.6PG 12.9repmgr 5.1.01.系统环境准备 主机名 ip地址 角色 端口 enmo-01 172.20.10.1 主库 6000 enmo-02 172.20.10.2 备库 6000 enmo-03 172.20.10.3 备库 6000准备将172.20.10.6设置为vip修改网卡(刷新一下 mac 地址)需要所有的机器在同一个网段
VM10装Mac OS X 10.9.3及更新到Mac OS X 10.10最近WWDC放出终极大招——新的编程语言Swift(雨燕),导致一大波程序员的围观和跃跃欲试。当然了,工欲善其事,必先利其器,所以对于那些没有Mac又想要尝鲜的小伙伴肯定很为难。但是,请放心,本文教你如何在Windows下也能体验Mac的滋味,当然咯,最主要的还是体验新的语言Swift。好了话不多说,直接开始,
空指针的表示方法有几种,在C中可以用NULL 和0来表示空指针,但C程序员一般习惯用NULL而不是0来表示,这是为了区别数字0;在C++中,传统上更喜欢使用0而不是NULL;为了更好的区分,C++11特意添加了一个新的关键字nullptr来表示空指针。...
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Diagnostics;using System.IO;using System.Security.Cryptography;namespace AESEncoder{...
在 M=m+1 个变数中,m个变数的综合和一个变数的相关,叫做多元相关或负相关(multiple correlation);其余M-2个变数皆固定时,指定的两个变数间相关,叫做偏相关(partial correalation)。从相关关系的性质看,多元相关和偏相关的M个变数都是随机变数,并无自变数和依变数之分。多元相关和偏相关的统计数也常用于有自变数和依变数之分的资料,并作为回归显著性...
一、写在前面继上篇博文,在这篇博文中说明自定义Horiozn,基于目前OpenStack rocky版本。邮箱地址:[email protected]个人博客:https://jianpengzhang.github.io/程序员秘密:http://blog.csdn.net/u011521019Horizon 原文阅读地址:https://docs.openstack.org/...
前言继一键启动zookeeper、hadoop、hbase集群(shell脚本)增添hive,spark组件一键启动文件名: start-cluster.shvim start-cluster.sh插入以下内容:#! /bin/bash echo -e "\033[31m ————————————————hadoop生态圈启动中————————————————\033[0m"sleep 1echo -e "\033[34m ————————————————启动zook.
L3-018森森美图(30分)森森最近想让自己的朋友圈熠熠生辉,所以他决定自己写个美化照片的软件,并起名为森森美图。众所周知,在合照中美化自己的面部而不美化合照者的面部是让自己占据朋友圈高点的绝好方法,因此森森美图里当然得有这个功能。 这个功能的第一步是将自己的面部选中。森森首先计算出了一个图像中所有像素点与周围点的相似程度的分数,分数越低表示某个像素点越“像”一个轮廓边缘上的点。 森森...
1)android 获取设备型号、OS版本号:import android.os.Build;// .....Build bd = new Build();String model = bd.MODEL;android.os.Build.MODELandroid.os.Build.VERSION.RELEASE2)android 获取设备Id等信息:1.需要在AndroidManifest.xml...
展望2022年,缺芯可能有所缓解,PC产品的更新或将加快步伐。行业内大家最期待的,莫过于预热时间超过一年的英特尔独显,N卡和A卡的斗争持续良久,Arc锐炫独立显卡能否打破局面备受关注。笔记本市场中,华为、小米、realme、Redmi等品牌的加入,也将为笔记本行业注入新鲜的血液,传统品牌们将面临更大的竞争。
1.什么是作用域插槽作用域插槽就是带数据的插槽, 就是让父组件在填充子组件插槽内容时也能使用子组件的数据2.如何使用作用域插槽2.1在slot中通过 v-bind:数据名称="数据名称" 方式暴露数据2.2在父组件中通过 <template slot-scope="作用域名称"> 接收数据2.3在父组件的<template></template>中通过 作用域名称.数据名称 方式使用数据-->...
小波作为一种信号处理的工具在脑波分析中应用很多,常用的有连续小波变换、小波包分析等等。小波涉及的相关介绍和公式推导有很多资料,文章末尾推荐了几个链接。本文主要介绍连续小波变换,小波包分解重构,对应频段能量计算这3种应用在Python中的实现。数据来源为BCI竞赛公开数据集中的部分数据,剔除了无效数据。有关数据的描述见链接:1、连续小波变换(主要用于时频域分析)这里使用连续小波变换进行时频域分析,数...