C++:std::move()和完美转发的仿,auto、decltype关键字,NULL和nullptr 的简单理解(笔记自用)

1.仿写std::move

// 1
template<typename _Ty>
struct my_remove_reference
{
	using type = _Ty;
	using _Const_thru_ref_type = const _Ty;
};
// 2
template<typename _Ty>
struct my_remove_reference<_Ty&>
{
	using type = _Ty;
	using _Const_thru_ref_type = const _Ty&;
};
// 3
template<typename _Ty>
struct my_remove_reference<_Ty&&>
{
	using type = _Ty;
	using _Const_thru_ref_type = const _Ty&&;
};

// 4
template<typename _Ty>
using my_remove_reference_t = typename my_remove_reference<_Ty>::type;

// 5
template<typename _Ty>
my_remove_reference_t<_Ty>&& my_move(_Ty&& _Arg)
{
	return static_cast<my_remove_reference_t<_Ty>&& >(_Arg);
}

原理:有三个版本的用作萃取的类模板

  1. 泛化版本,用type表示原始类型;
  2. 接收引用的部分特化版本,用type表示原始类型;
  3. 接收右值引用的部分特化版本,依旧用type表示原始类型。
  4. 再通过第四个模板类 获取原始类型type,将其重命名为my_remove_reference。
  5. 此时第五个模板类的_Ty就表示最原始的类型,然后利用static_cast将其强转为右值引用,但这里面无法去除常性(const)。

2.完美转发

move语义是不管传入什么值,都萃取处原始类型,完美转发是变量原本是什么类型,在经过一系列函数调用后还保留原始属性(左值右值,纯右值)。

template<typename _Ty>
_Ty&& my_forward(my_remove_reference_t<_Ty>& _Arg) noexcept
{
	return static_cast<_Ty&&>(_Arg);
}

template<typename _Ty>
_Ty&& my_forward(my_remove_reference_t<_Ty>&& _Arg) noexcept
{
	return static_cast<_Ty&&>(_Arg);
}

示例:

void print(int& a)
{
	cout << "int & " << endl;
}
void print(const int& a)
{
	cout << "const int & " << endl;
}
void print(int&& a)
{
	cout << "int &&" << endl;
}

template<typename _Ty>
void fun(_Ty&& a)
{
	print(my_forward<_Ty>(a));
}

int Add(int a, int b)
{
	return a + b;
}

int main(void)
{
	int a = 10;
	int& b = a;
	const int& c = a;
	int&& d = 10;
	fun(a);
	fun(b);
	fun(c);
	fun(d);
	fun(20);
	fun(Add(a, b));
	return 0;
}

在这个例子中,主函数调用的6个fun() 函数,传入的参数经过完美转发仍然保持其原本属性。

  1. 为什么d引用了纯右值,但却打印的 “int&”,因为引用了纯右值,这个纯右值就具有了名字,可以对其寻址,那它已经是左值了。

  2. 为什么最后一个fun()函数调用的Add()函数,传入的左值,但打印结果是右值? 因为在Add()函数return时是返回了一个将亡值,对其按照右值的特性来看,所以调用的右值的print().

在这里插入图片描述

3. auto关键字

  1. auto在C++中的意义和C语言中的不同,它是表示自动类型推演。

可以这样用:

auto x = 10; // x->int
auto f = 12.23; // f->double
auto ip = new int(10); // ip->int*
auto* op = new int(10); // op->int
  1. auto在使用时不可以“分身”,示例:前面一个变量推演出auto是int,但变量u是浮点型,auto不能推演,所以说不能分身。

原因就是auto在编译过程中就会替换成推演到的类型,执行到u这一步时类型不匹配了。

在这里插入图片描述

  1. 对常变量的推演
    如果是拿常性变量给auto类型的变量赋值,那么auto默认是整型的;
    但如果是auto类型的引用,那么auto也会推演成常性变量(const int)
const int x = 10;
auto a = x; // auto->int
auto& b = x; // auto ->const int
  1. auto的限制:1.不允许作为函数参数;2.不能以auto声明类里的数据成员(除非是静态常性的整型);3.不能声明集合(例如数组这样的)。

示例:

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

4. decltype

这个关键字也是类型推演,推演的是表达式的类型。可以利用这个特点声明变量。示例:

int main(void)
{
	int a = 10;
	decltype(a) b = 20;

	decltype(a + b) c = 10;
}

在这里插入图片描述

5. NULL和nullptr

先来看看NULL的定义是怎样的:
如果在Cpp中,定义NULL为0,否则为无类型指针。

#ifndef NULL
    #ifdef __cplusplus
        #define NULL 0
    #else
        #define NULL ((void *)0)
    #endif
#endif

那么在下面这个例子中就会产生二义性:

void print(char* a)
{
	cout << "char* a" << endl;
}
void print(int a)
{
	cout << "int a" << endl;
}
int main(void)
{
	print(NULL);
	print(0);
	return 0;
}

可以发现传进去的参数都是整型,就是把NULL当成0来处理了。
在这里插入图片描述

再来看nullptr:

void print(char* a)
{
	cout << "char* a" << endl;
}
void print(int a)
{
	cout << "int a" << endl;
}
int main(void)
{
	print(nullptr);
	print(NULL);
	print(0);
	return 0;
}

在这里插入图片描述

原因是nullptr是C++中的一个关键字,意思是一个空指针常量。来看看它是怎么定义的:转到头文件<stddef.h>

#ifdef __cplusplus
    namespace std
    {
        typedef decltype(__nullptr) nullptr_t;
    }

    using ::std::nullptr_t;
#endif

在该头文件中可以看到nullptr的定义,最骚的是,这个东西竟然是先有值,再有这种类型,意思是可以这样定义变量了?示例:

int main(void)
{
	typedef decltype(nullptr) nullptr_t;
	nullptr_t a;

	return 0;
}

在调试过程中发现,定义的变量就是无类型指针(void*)。。。
在这里插入图片描述

end

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_索伦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值