变长数组

特点:

  • 变长
  • 空间合理

eg:

struct MutableLenArray 
{ 
	int count; 
	char p[0]; 
};

宏的妙用

1.#和##

“#”符号把一个符号直接转换为字符串,例如:

#define TO_STRING(x) #x 
const char *str = TO_STRING( test );

#define P(A) printf("%s:%d\n",#A,A);
int a = 1;

P(a) 
//	a:1 


##符号会连接两个符号,从而产生新的符号(词法层次),例如:

#define SIGN( x ) INT_##x 
int SIGN( 1 );
//宏被展开后将成为:int INT_1;

2. 变参宏

#define LOG( format, ... ) printf( format, __VA_ARGS__ ) 
LOG( "%s %d", str, count );

#define LOG(format, args...) fprintf(stdout, format, args)

  • VA_ARGS是系统预定义宏,被自动替换为参数列表。
  • 经常需要进行输出格式化,重定义时,可以用到以上技巧。
  • 同样,args在预处理过程中,会被实际的参数集所替换。其用法和上面的方式一样,只是参数的符号有变。
  • 需要注意的是,上述两种方式的可变参数不能省略,尽管可以传一个空参数进去。
  • 说到这里,有必要提一下“##”连接符号的用法,“##”的作用是对token进行连接,上例中format,args,__VA_ARGS都可以看作是token,如果token为空,“##”则不进行连接,所以允许省略可变参数。
#define LOG(format, ...) fprintf(stdout, format, ##__VA_ARGS__)  
#define LOG(format, args...) fprintf(stdout, format, ##args)

#define LOG(format, ...) fprintf(stdout, ">>>>>" format "<<<<", ##__VA_ARGS__) 
#define LOG(format, ...) 

变参宏个数

#define comac_arg_n( _0,_1,_2,_3,_4,_5,_6,_7,N,...) N
#define comac_get_args_cnt( ... ) comac_arg_n( __VA_ARGS__ )
#define comac_argc(...) comac_get_args_cnt( 0, ##__VA_ARGS__, 7,6,5,4,3,2,1,0)



comac_argc(arg1,arg2,arg3)

comac_get_args_cnt(0,arg1,arg2,arg3, 7,6,5,4,3,2,1,0)

comac_arg_n(0 ,arg1,arg2,arg3, 7 , 6, 5, 4,3,2,1,0)
		   (-0,  _1, _2, _3,   _4,_5,_6,_7,N)    N //(对称性) 此时n为3
    			

#define D(_1, _2, _3, _4,   _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, N, ...)  N
#define D2(args...) D(args, 15, 14, 13, 12, 11,  10,   9,   8,   7,   6,   5,   4, 3, 2, 1, 0)
    
D2(1,2,3,4)  ;//此时N为   4
    
    

eg:

{DBG_MAP(DBG_STRINGIFY, __VA_ARGS__)}
// DBG_MAP(fn, e1, e2, e3, ...) => fn(e1), fn(e2), fn(e3), ...

#define DBG_STRINGIFY_IMPL(x) #x
#define DBG_STRINGIFY(x) DBG_STRINGIFY_IMPL(x)


#define DBG_MAP(fn, ...) DBG_VARIADIC_CALL(DBG_MAP, fn, __VA_ARGS__)
#define DBG_VARIADIC_CALL(fn, data, ...) DBG_CAT(fn##_, DBG_NARG(__VA_ARGS__))(data, (__VA_ARGS__))
#define DBG_CAT(_1, _2) DBG_CAT_IMPL(_1, _2)
#define DBG_CAT_IMPL(_1, _2) _1##_2

#define DBG_NARG(...) DBG_16TH((__VA_ARGS__, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0))
#define DBG_16TH(args) DBG_CALL(DBG_16TH_IMPL, args)
#define DBG_CALL(fn, args) DBG_IDENTITY(fn args)
#define DBG_IDENTITY(x) x


#define DBG_16TH_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, ...)  _16


DBG_VARIADIC_CALL(DBG_MAP, DBG_STRINGIFY, __VA_ARGS__)
DBG_CAT(DBG_MAP_, DBG_NARG(__VA_ARGS__))(DBG_STRINGIFY, (__VA_ARGS__))



DBG_MAP_[DBG_NARG(__VA_ARGS__)]

DBG_MAP_[DBG_16TH((__VA_ARGS__, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0))]

DBG_MAP_[DBG_CALL(DBG_16TH_IMPL, (__VA_ARGS__, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0))]

DBG_MAP_[DBG_IDENTITY(DBG_16TH_IMPL (__VA_ARGS__, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0))]

DBG_MAP_[DBG_16TH_IMPL (__VA_ARGS__, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)]

DBG_MAP_[参数个数](DBG_STRINGIFY,(__VA_ARGS__))

// (e1, e2, e3, ...) => e1
#define DBG_HEAD_IMPL(_1, ...) _1
#define DBG_HEAD(args) DBG_CALL(DBG_HEAD_IMPL, args)


// (e1, e2, e3, ...) => (e2, e3, ...)
#define DBG_TAIL_IMPL(_1, ...) (__VA_ARGS__)
#define DBG_TAIL(args) DBG_CALL(DBG_TAIL_IMPL, args)


//此时定义 便可以用fn 来遍历所有参数

#define DBG_MAP_1(fn, args) DBG_CALL(fn, args)
#define DBG_MAP_2(fn, args) fn(DBG_HEAD(args)), DBG_MAP_1(fn, DBG_TAIL(args))
#define DBG_MAP_3(fn, args) fn(DBG_HEAD(args)), DBG_MAP_2(fn, DBG_TAIL(args))
#define DBG_MAP_4(fn, args) fn(DBG_HEAD(args)), DBG_MAP_3(fn, DBG_TAIL(args))
#define DBG_MAP_5(fn, args) fn(DBG_HEAD(args)), DBG_MAP_4(fn, DBG_TAIL(args))
#define DBG_MAP_6(fn, args) fn(DBG_HEAD(args)), DBG_MAP_5(fn, DBG_TAIL(args))
#define DBG_MAP_7(fn, args) fn(DBG_HEAD(args)), DBG_MAP_6(fn, DBG_TAIL(args))
#define DBG_MAP_8(fn, args) fn(DBG_HEAD(args)), DBG_MAP_7(fn, DBG_TAIL(args))
#define DBG_MAP_9(fn, args) fn(DBG_HEAD(args)), DBG_MAP_8(fn, DBG_TAIL(args))
#define DBG_MAP_10(fn, args) fn(DBG_HEAD(args)), DBG_MAP_9(fn, DBG_TAIL(args))
#define DBG_MAP_11(fn, args) fn(DBG_HEAD(args)), DBG_MAP_10(fn, DBG_TAIL(args))
#define DBG_MAP_12(fn, args) fn(DBG_HEAD(args)), DBG_MAP_11(fn, DBG_TAIL(args))
#define DBG_MAP_13(fn, args) fn(DBG_HEAD(args)), DBG_MAP_12(fn, DBG_TAIL(args))
#define DBG_MAP_14(fn, args) fn(DBG_HEAD(args)), DBG_MAP_13(fn, DBG_TAIL(args))
#define DBG_MAP_15(fn, args) fn(DBG_HEAD(args)), DBG_MAP_14(fn, DBG_TAIL(args))

3.不定参

模板实现:

template<typename... T>
void func(T... args)
{
    for (auto x : {args...})
    {
        cout << x <<endl;
    }
}

单个使用

void func_()
{
    cout <<endl;
}
template<typename T, typename... U>
void func_(T &&value, U &&... rest) //-> last_t<T, U...>
{
    cout << value << endl;
    func_(std::forward<U>(rest)...);
}

// int x = 1, y = 3, z = 6, a = 10; func_(x,y,z,a);
//	1
//	3
//	6
//	10
//

4. note

__func__
已被添加到C ++ 11中的C ++中,其中被指定为包含“实现定义的字符串”(C ++ 11§8.4.1[dcl.fct.def.general] / 8),可用作C中的规范。(最初添加__func__到C ++中的建议是N1642)。

__FUNCTION__
是某些C编译器支持的标准扩展(包括gcc和Visual C ++);通常,应该__func__在受支持的地方使用它,并且仅__FUNCTION__在使用不支持它的编译器时才使用(例如,不支持C99并且尚不支持所有C ++ 0x的Visual C ++不支持)。提供__func__)。

__PRETTY_FUNCTION__
是gcc扩展名,与__FUNCTION__g ++ 扩展名几乎相同,除了C ++函数外,它包含函数的“漂亮”名称,包括函数的签名。Visual C ++具有类似(但不完全相同)的扩展__FUNCSIG__。

__DATE__
源文件的翻译日期:字符字符串文字,格式为" Mmm dd yyyy" ,其中月份的名称与asctime函数生成的月份的名称相同,并且dd的第一个字符(如果值小于10)为空格字符。没有可用的
,则应提供实现定义的有效日期。

__FILE__
当前源文件的假定名称(字符字符串文字)。

__TIME__
翻译的时间源文件:一个字符串字符串文字,格式为" hh:mm:ss" ,与asctime函数生成的时间相同。

do…while(0)

在一些函数中,我们在return语句之前可能需要做一些工作,比如释放在函数一开始由malloc函数申请的内存空间,使用goto总是一种简单的方法:

int foo() 
{ 
    somestruct* ptr = malloc(...); 
    
    dosomething...; 
    if(error) 
    { 
        goto END; 
    } 
    
    dosomething...; 
    if(error) 
    { 
        goto END; 
    } 
    dosomething...; 
    
END: 
    free(ptr); 
    return 0; 
    
} 

但由于goto关键字可能会使代码不易读,因此许多人都不推荐使用它,那么我们可以使用do{…}while(0)来解决这一问题:

int foo() 
{ 
    somestruct* ptr = malloc(...); 
    
    do{ 
        dosomething...; 
        if(error) 
        { 
            break; 
        } 
    
        dosomething...; 
        if(error) 
        { 
            break; 
        } 
        dosomething...; 
    }while(0); 
    
    free(ptr); 
    return 0; 
    
} 

这里,我们使用do{…}while(0)来包含函数的主要部分,同时使用break替换goto,代码的可读性增强了。

type_traits

traits是c++模板编程中使用的一种技术,主要功能: 把功能相同而参数不同的函数抽象出来,通过traits将不同的参数的相同属性提取出来,在函数中利用这些用traits提取的属性,使得函数对不同的参数表现一致。

traits是一种特性萃取技术,它在Generic Programming中被广泛运用,常常被用于使不同的类型可以用于相同的操作,或者针对不同类型提供不同的实现.traits在实现过程中往往需要用到以下三种C++的基本特性: enum、typedef、template (partial) specialization 其中: enum用于将在不同类型间变化的标示统一成一个,它在C++中常常被用于在类中替代define,你可以称enum为类中的define; typedef则用于定义你的模板类支持特性的形式,你的模板类必须以某种形式支持某一特性,否则类型萃取器traits将无法正常工作 template (partial) specialization被用于提供针对特定类型的正确的或更合适的版本.

1. 基本的type_traits

1.1 简单的type_trait

定义一个编译期常量
template<typename T>
struct GetLeftSize{
    //使用静态常量
    static const int value = 1;
    //或者使用 enum
    enum{value = 1};
};
 
c++11中直接继承 std::integral_constant即可
template<typename T>
struct GetLeftSize : std::integral_constant < int, 1 >
{
};
int main(){
    cout << GetLeftSize<float>::value << endl;
    return 0;
}
 
std::integral_constant的实现:
    // TEMPLATE CLASS integral_constant
template<class _Ty,
    _Ty _Val>
    struct integral_constant
    {   // convenient template for integral constant types
    static const _Ty value = _Val;
 
    typedef _Ty value_type;
    typedef integral_constant<_Ty, _Val> type;
 
    operator value_type() const
        {   // return stored value
        return (value);
        }
    };

1.2 类型判断的type_traits

template<typename T>
struct is_integral; //用来检查T是否为bool、char、char16t_t、char32_t、short、long、long long或者这些类型的无符号整数类型。如果T是这些类型中的某一类型,则std::is_integral::value 为true, 否则为false。
其他的一些类型判断type_traits
template<typename T>
struct is_void; //是否为void类型
template<typename T>
struct is_floating_point; //是否为浮点类型
is_const, is_function, is_pointer, is_compound....
std::is_const<int>::value //false
std::is_const<const int>::value //true

1.3 判断两个类型之间关系的traits

traits 类型 说明
template< typename T, typename U>struct is_same; 判断两个类型是否相同
template< typename T, typename U>struct is_base_of; 判断类型T是否是类型U的基类
template< typename T, typename U>struct is_convertible; 判断类型T能否转换为类型U

1.4 类型的转换 traits

traits类型	说明
template< typename T>
struct remove_const;	移除const
    
template< typename T>
struct add_const;	添加const
    
template< typename T>
struct remove_reference;	移除引用
    
template< typename T>
struct add_lvalue_reference;	添加左值引用
    
template< typename T>
struct add_rvalue_reference;	添加右值引用
    
template< typename T>
struct remove_extent;	移除数组顶层的维度,
//比如 int [3][3][2] 变为 int [3][2]
    
template< typename T>
struct remove_all_extent;	移除数组所有的维度,比如 int [3][3][2] 变为 int
    
template< typename T>
struct remove_pointer;	移除指针
    
template< typename T>
struct add_pointer;	添加指针
    
template< typename T>
struct decay;	移除cv或者添加指针
    
template< typename .... T>
struct common_type;	获取公共类型

    //通过 ::type来访问这些类型。
    
    
std::cout << std::is_same<const int, std::add_const<int>::type>::value << endl; //结果为true
std::cout << std::is_same<int, std::remove_all_extent<int[2][2][3]>::type>::value<<endl;
 
//在根据模板参数创建对象时,要注意移除引用:
template<typename T>
typename std::remove_reference<T>::type* Create(){
    typedef typename std::remove_reference<T>::type U;
    return new U();
}
//因为模板参数可能是引用类型,而创建对象时,需要原始的类型,不能用引用类型,所以需要将可能的引用移除
 
//如果给的模板参数是一个带cv描述符的引用类型,要获取它的原始类型,可以使用decay
template<typename T>
typename std::decay<T>::type* Create(){
    typedef typename std::decay<T>::type U;
    return new U();
}
 
decay还可以获得函数的指针类型,从而将函数指针变量保存起来,以便在后面延迟调用。
typdef std::decay<int(double)>::type F; //F为一个函数指针类型, int(*)(double)
template<typename F>
struct SimpleFunction{
    using FnTyppe = typename std::decay<F>::type;
    SimpleFunction(F& f): m_fn(f){};
    void Run(){
        m_fn();
    }
     
    FnType m_fn;
};

2. 根据条件选择的traits

std::conditional在编译期根据一个判断式选择两个类型中的一个,和条件表达式的语义类似,类似于一个三元表达式:

template<bool B, class T, class F>
struct conditional;
//在std::conditonal模板参数中,如果B为true,则conditional::type为T,否则为F。
std::conditional<true, int, double>::type //= int

3. 获取可调用对象返回类型的traits

//返回类型后置
template<typename F, typename Arg>
auto Func(F f, Arg arg)->decltype(f(arg)){
    return f(arg);
}

 //c++11提供了另一个traits——result_of,用来在编译期获取一个可调用对象的返回类型。
template<typename F, class... ArgTypes>
class result_of<F(ArgTypes...)>;
 

int fn(int) {return int();};
typedef int(&fn_ref)(int);
typedef int(*fn_ptr)(int);
struct fn_class{
    int operator()(int i){
        return i;
    }
};
int main(){
    typedef std::result_of<decltype(fn)&(int)>::type A;  //int
    typedef std::result_of<fn_ref(int)>::type B;        //int
    typedef std::result_of<fn_ptr(int)>::type C;        //int
    typedef std::result_of<fn_class(int)>::type D;      //int
    return 0;
}
//需要注意 std::result_of<Fn(ArgTypes)> 要去Fn为一个可调用对象,而函数类型不是一个可调用对象,因此,不能使用
typedef std::result_of<decltype(fn)(int)>::type A:  //错误
//需要先将fn转换为一个可调用对象类型,比如:
typedef std::result_of<decltype(fn)&(int)>::type A;
typedef std::result_of<decltype(fn)*(int)>::type B;
typedef std::result_of<std::decay<decltype(fn)>::type(int)>::type C;

4. 根据条件禁用或启用某种或某些类型traits

template< bool B, class T = void>   //T为返回类型,常用作函数的返回类型
struct enable_if;
```
`当B为true的时候,返回类型T,否则编译出错。`
```
template<class T>       //T只有为合法的类型,才能调用该函数,否则编译出错
typename std::enable_if<std::is_arithmetic<T>::value, T>::type foo(T t){
    return t;
}
auto r = foo(1);    //返回1
auto r1 = foo(1.2); //返回1.2
auto r2 = foo("hello"); //编译出错

5.SFINAE

template <typename T>
struct has_type {
private:
    typedef char one;
    typedef struct { char data[2]; } two;
    // 存在的话返回类型为 one
    template <typename U> static one test(typename U::type*);
    // 不存在的话返回类型为 two
    template <typename U> static two test(...);
public:
    enum { value = sizeof(test<T>(0)) == sizeof(one) };
};

如果 T::type 存在的话就会选择第一个重载,否则就会选择第二个重载,由此判断 T::type 是否存在。但是这样的代码阅读起来可能会挺费劲的……于是,现在有了 void_t!

6. is_detected

//下面是用 is_detected 判断成员函数是否存在:
template <typename T>
using has_type_t = typename T::type;
template <typename T>
using has_type = is_detected<has_type_t, T>;


template <typename, template <typename...> class Op, typename... T>
struct is_detected_impl : std::false_type {};
template <template <typename...> class Op, typename... T>
struct is_detected_impl<void_t<Op<T...>>, Op, T...> : std::true_type {};

template <template <typename...> class Op, typename... T>
using is_detected = is_detected_impl<void, Op, T...>;

模板返回类型

    template<class OutputType = std::string>
    OutputType get(int idx)
    {
        std::stringstream ss;
        ss << args_vec[idx];
        OutputType ret;
        ss >> ret;
        return ret;
	}

把函数对象直接放到类中

class AAA{
public:
    int lastname;
    // 比大小
    function<bool(const BBB &p1, const BBB &p2)> cmp = [](const BBB &p1, const BBB &p2) {
        return p1.lastname< p2.lastname;
    };

    // 对于lambda,我们往往只有object,很少有人能够写出它的类型,而有时就需要知道它的类型,要获得其type,就要借助其decltype
    set<BBB, decltype(cmp)> col;

};