美文网首页
C++模板实用技巧

C++模板实用技巧

作者: DayDayUpppppp | 来源:发表于2021-10-30 10:41 被阅读0次

整理C++模板在开发中的一些使用技巧

  1. 编译期静态检查
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
}
  1. 实现可以检查类型的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));
}
  1. 根据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;
}
  1. 根据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;
}
  1. 根据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;
}
  1. 根据是否有某个成员函数匹配模板
#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);
}
  1. 根据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;
}

  1. 关于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就失败了 —— 所以这个函数原型根本就不会被产生出来。


  1. 对象计数
    利用模板实现对象计数

  1. 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()找不到对应的符号,提示链接错误。

所以,对于模板类的多文件组织,需要模板类的实现和定义放在一起。第三方编译单元include的时候,同时拿到定义和实现。比如,修改为这样: 同理,模板函数也是同样的问题,需要把定义和实现都放在.h文件中,比如:

参考:
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

相关文章

网友评论

      本文标题:C++模板实用技巧

      本文链接:https://www.haomeiwen.com/subject/jdopaltx.html