1,using可重定义一个模板,该模板既不是类模板也不是函数模板,是一种新的模板形式:模板别名
templte <typename T>
using func_t=void (*)(T,T);
//使用func_t模板
func_t<int> xxx_2;
2,函数模板的参数在使用上与其他默认函数略不同,它没有必须写在参数表最后的限制。
template <typename R = int , typename U>
R func(U val)
{
val;
}
int main(void)
{
func(123);
return 0;
}
但在调用函数模板的时候,若显示指定了模板的参数,由于参数填充顺序是从左到右的,因此下面这样 调用函数模板返回的是long,不是int
func(123); //func的返回值类型是long
3,当默认模板参数和模板参数自动推导同时使用时,若函数模板无法自动推导出参数类型,则编译器将使用默认模板参数;否则将使用自动推导出的参数类型。
优先级:自动推导出的参数类型 > 默认模板参数
4,POD类型
粗略上来讲,POD是C++为兼容C的内存布局而设计的,主要用于修饰用户自定义类型。
但POD却远比这个要复杂。POD(Plain Old Data),从字面意思上来看,是平凡普通的老旧格式的数据,POD是一个类型属性,既不是关键字也不会像”volatile”用来修饰类型信息。
POD类型,说明该数据是普通的,不会有什么虚函数啊,虚继承啊,或者内嵌的数据类型很复杂的情况。也就是说,POD类型的C++变量,可以直接用C语言中的struct来解释
直观的来说,POD类型中的O代表了与C语言的兼容性,可以直接使用memcpy()直接复制而不会出现任何问题。POD这个想法的由来就是要支持两种最基本的属性:
(1)支持静态初始化
(2)编译C++中的POD类型所得到的内存布局,和C中编译struct的内存布局相同
具体地,C++11将POD类型划分为两个概念:
trivial
standard layout
在C++11中,更多的是用trivial和standard类代替POD类型。
trivial意思是无意义,这个trivial和non-trivial是对类的四种函数来说的:
构造函数(ctor)
复制构造函数(copy)
赋值函数(assignment)
析构函数(dtor)
如果至少满足下面3条里的一条:
显式(explict)定义了这四种函数。
类里有非静态非POD的数据成员。
有基类。
那么上面的四种函数是non-trivial函数
5,在使用初始化列表时,什么样的类型C++会认为它是一个聚合体
(1)类型是一个普通数组
(2)类型是一个类,且
无用户自定义的构造函数
无私有或者保护的非静态数据成员
无基类
无虚函数
不能有{}和=直接初始化的非静态数据成员
聚合类型的定义并非递归的,当一个类的非静态成员是非聚合类型时,这个类也有可能是聚合类型,可以列表初始化。
6,std::initializer_list拥有与一个无参数的构造函数;同时它是非常高效的,它只保存列表中的数据的引用。因此下面的做法是错误的:
std::initializer_list<int> func()
{
int a =1,b=2;
return {a,b};
}
这样虽然可以通过编译,但是无法得到希望的结果,a,b的生命周期在调用后结束,引用的将是不确定的内容。
可以改为:
std::vector<int> func()
{
int a =1,b=2;
return {a,b};
}
7,列表初始化对于类型收窄会报错
8,可调用对象
(1)函数指针
(2)具有operator()成员函数的类对象
(3)一个可被转为函数指针的类对象
(4)一个类成员指针
可调用类型的定义不包括函数类型或者函数引用(只有函数指针),因为函数类型不能直接用来定义对象;而函数引用从某种意义上来说可以看做是一个const的函数指针。
9,std::function是可调用对象的包装器。它是一个类模板,可以容纳除了类成员(函数)指针之外的所有可调用对象。
10,std::bind的返回类型是一个stl内部定义的仿函数类型,可以直接赋值给一个std::function
auto newCallable=bind(callable.arg_list)
newCallable本身是一个可调用对象
例子:
#include <iostream>
#include <functional>
void output(int x,int y){
std::cout<<x<<" "<<y<<std::endl;
}
int main(int argc, char **argv) {
std::bind(output,1,2)();// 输出1 2
//等价于
auto f=std::bind(output,std::placeholders::_1,std::placeholders::_2);
f(1,2); //输出1 2
//等价于
std::bind(output,std::placeholders::_1,std::placeholders::_2)(1,2);// 输出1 2
std::bind(output,std::placeholders::_1,2)(1); //输出 1 2
std::bind(output,2,std::placeholders::_1)(1); //输出2 1
//std::bind(output,2,std::placeholders::_2)(1); //error 调用时没有第二个参数
std::bind(output,2,std::placeholders::_2)(1,2); //输出 2 2
std::bind(output,std::placeholders::_2,std::placeholders::_1)(1,2); //输出 2 1
}
结果:
1 2
1 2
1 2
1 2
2 1
2 2
2 1
11,关于std::bind的一些用法总结
最近在看《深入应用c++11》的时候遇到了std::bind的一些新用法,之前没有遇到过,这里记录下。通常时候std::bind是与std::function一起结合使用的,std::bind是一个函数模板,而std::function是一个类模板,这个从其源码就可看出
/**
* @brief Function template for std::bind.
* @ingroup binders
*/
template<typename _Func, typename... _BoundArgs>
inline typename
_Bind_helper<__is_socketlike<_Func>::value, _Func, _BoundArgs...>::type
bind(_Func&& __f, _BoundArgs&&... __args)
{
typedef _Bind_helper<false, _Func, _BoundArgs...> __helper_type;
typedef typename __helper_type::__maybe_type __maybe_type;
typedef typename __helper_type::type __result_type;
return __result_type(__maybe_type::__do_wrap(std::forward<_Func>(__f)),
std::forward<_BoundArgs>(__args)...);
}
template<typename _Signature>
class function;
std::bind的常见用法
之前的理解仅仅是将bind当做绑定一个全局函数,现在又遇到了一些其他的用法,直接上代码吧
/*
* main.cpp
*
* Created on: 2017年12月3日
* Author: Administrator
*/
#include <iostream>
#include <functional>
void output(int x, int y)
{
std::cout << x << " " << y << std::endl;
}
class A
{
public:
int i_ = 0;
void output(int x, int y)
{
std::cout << x << " " << y << std::endl;
}
void operator()() const //无参数 无返回值的仿函数
{
std::cout <<"仿函数"<< std::endl;
}
};
int main(void)
{
//1,绑定全局函数
std::bind(output,1,2); //全局函数,仅仅是绑定,没有调用
std::bind(output,1,2)();//绑定并调用全局函数
//2,绑定成员函数
A a;
std::function<void(int, int)> fr =
std::bind(&A::output, &a /*调用者*/, std::placeholders::_1, std::placeholders::_2);
fr(1, 2); //输出 1 2
//3,绑定成员变量(包括静态的成员变量) 成员变量这里function中是int&(void)
std::function<int&(void)> fr_i = std::bind(&A::i_, &a/*调用者*/); //vs13的bug,绑定成员变量要报错
fr_i() = 123;
std::cout << a.i_ << std::endl; //输出 123
std::cout << fr_i() << std::endl; //输出 123
A a1;
std::function<int&(void)> fr_i_1 = std::bind(&A::i_, a1/*调用者*/); //调用者没有传指针
fr_i_1() = 123;
std::cout << a1.i_ << std::endl; //输出 0
std::cout << fr_i_1() << std::endl; //输出 123
//4,仿函数
std::function<void(void)> op = std::bind(&A::operator(),a);
op();
return 0;
}
结果如下:
1 2
1 2
123
123
0
123
仿函数
12,lambda表达式通过捕获列表捕获一定范围内的变量
[]不捕获任何变量
[&]捕获外部作用域中所有变量,并作为引用在函数体内引用
[=]捕获外部作用域中所有变量,按值捕获
[=,&foo]按值捕获外部作用域的所有变量,并按引用捕获foo变量
[bar] 仅仅只值捕获bar变量
[this]捕获当前类中的this指针,让lambda表达式拥有和当前类成员函数同样访问权限。如果已经使用了&或者=,就默认添加此项。
捕获this的目的是可以在lambda中使用当前类的成员函数和成员变量
13,lambda无法修改通过赋值方式捕获的外部变量。
易出错点:lambda的延时调用,注意值捕获与引用捕获的时候,延时调用的区别
14,lambda的类型在c++11中被称为“闭包类型”,可认为其是一个仿函数。
std::function<int(int)> f1=[](int a){return a;};
std::function<int(int)> f2=std::bind([](int a){return a;},123);
std::function<int(void)> f3=std::bind([](int a){return a;},123);
这里函数f2和f3都是绑定的同一个函数,但是f2却是带有int参数,f3无参数
15,没有捕获变量的lambda表达式可以直接转换为函数指针,而捕获变量的lambda表达式则不能转换为函数指针。
16,按c++的标准,lambda的operator()默认是const,如果是按值捕获的话,const成员函数是无法修改成员变量的值,而mutable的作用就是取消operator()的const属性。
17,tuple简单的创建与取值方式
//创建方式1
tuple<const char*,int> tp=make_tuple("123",1);
//创建方式2
int x=1;
int y=2;
string s="aa";
auto tp1=std::tie(x,s,y);//tp的类型实际为std::tuple<int&,string&,int&>
//取值方式1
const char * data = tp.get<0>(); //获取第一个值
int len=tp.get<1>(); //获取第二个值
//取值方式2
int x,y;
string a;
std::tie(x,a,y) = tp1;
std::tie(std::ignore,std::ignore,y) =tp1; //只解第三个值
其中std::tie创建的是一个元组的左值引用