整理C++模板在开发中的一些使用技巧
- 编译期静态检查
template<bool> // generic
struct CompileChecker;
template<>
struct CompileChecker<true> {};
#define static_assert(expr) {CompileChecker<expr != 0> obj;}
int main()
{
static_assert(sizeof(int) > sizeof(char)); // ok
static_assert(sizeof(int) < sizeof(char)); // fails to compile
}
- 实现可以检查类型的memcpy (避免手滑拷贝了两个不一样长度的对象)
template<typename T, typename U>
void safe_memcpy(T *dest, const U * src)
{
static_assert(sizeof(T) == sizeof(U));
memcpy(dest, src, sizeof(T));
}
- 根据T是否是某个类的派生类
#include <iostream>
#include <type_traits>
#include <utility>
#include <cstdint>
class BASE{};
class DERIVED : public BASE
{
public:
int show() {return 1;}
};
template<typename T>
void func(T t
, typename std::enable_if<std::is_base_of<BASE, T>::value>::type* = nullptr)
{
cout << t.show() << endl;
}
int main()
{
DERIVED obj;
func(obj);
return 0;
}
- 根据T是不是int匹配模板
#include <iostream>
#include <type_traits>
#include <utility>
#include <cstdint>
using namespace std;
template<typename T>
void func(T t
, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr)
{
t++;
cout << t << endl;
}
int main()
{
int val = 100;
func(val);
return 0;
}
- 根据T的是否有某个成员匹配模板
struct TEST_DATA
{
typedef int mytype;
int i_val1;
int i_val2;
};
template<typename T>
void build(typename T::mytype u)
{
std::cout<<"haha" << std::endl;
}
int main()
{
build<TEST_DATA>(123);
return 0;
}
- 根据是否有某个成员函数匹配模板
#include <iostream>
#include <type_traits>
#include <utility>
#include <cstdint>
struct TEST_OBJ
{
void show()
{
std::cout << "show " << std::endl;
}
};
template <typename T>
void show(T& obj
, std::decay_t<decltype(obj.show())>* = nullptr)
{
obj.show();
}
template <typename T>
void show(T& obj
, std::decay_t<decltype(obj++)>* = nullptr)
{
obj++;
std::cout << obj << std::endl;
}
int main()
{
TEST_OBJ obj;
show(obj);
int val = 0;
show(val);
}
- 根据T的大小匹配不同的模板
template<typename T>
void myfunc(T t
, typename std::enable_if<sizeof(T) <= 4>::type* = nullptr)
{
cout << " <= 4" << endl;
}
template<typename T>
void myfunc(T t
, typename std::enable_if<sizeof(T) >= 8>::type* = nullptr)
{
cout << " >= 8" << endl;
}
int main()
{
int val = 100;
myfunc(val);
struct TEST_DATA
{
int val[100];
};
TEST_DATA obj;
myfunc(obj);
return 0;
}
-
关于enable if
首先,substitution只有在推断函数类型的时候,才会起作用。推断函数类型需要参数的类型,所以,typename std::enable_if<std::is_integral<T>::value>::type 这么一长串代码,就是为了让enable_if参与到函数类型中。其次,is_integral<T>::value返回一个布尔类型的编译器常数,告诉我们它是或者不是一个integral。enable_if<C>的作用就是,如果这个C值为True,那么type就会被推断成一个void或者是别的什么类型,让整个函数匹配后的类型变成 void inc_counter<int>(int & counterInt, void* dummy = nullptr); 如果这个值为False,那么enable_if<false>这个特化形式中,压根就没有这个::type,于是substitution就失败了 —— 所以这个函数原型根本就不会被产生出来。
- 对象计数
利用模板实现对象计数
- C++ 类模板的多文件组织问题
在C++中会把类的实现文件和定义文件拆分,目的是保持结构清晰和提高编译速度。如果使用模板类之后,这种的涉及会导致链接问题。
// 链接报错如下:
$g++ main.cpp someclass.cpp
/usr/bin/ld: /tmp/cc7ZHNHI.o: in function `main':
main.cpp:(.text+0x10): undefined reference to `MYTemp<int>::MYTemp()'
collect2: error: ld returned 1 exit status
问题的根本原因是,在链接的时候someclass.o
文件里面的模板是不知道需要实例化出一个T = int的类型的,所以在ld mian.o someclass.o
的时候,MYTemp<int>::MYTemp()
找不到对应的符号,提示链接错误。
参考:
https://www.jianshu.com/p/20577b8d273e
https://zhuanlan.zhihu.com/p/21314708
https://www.bilibili.com/read/cv10615073?spm_id_from=333.999.0.0
网友评论