栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

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

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

文章目录
  • 1.仿写std::move
  • 2.完美转发
  • 3. auto关键字
  • 4. decltype
  • 5. NULL和nullptr
  • end

1.仿写std::move
// 1
template
struct my_remove_reference
{
	using type = _Ty;
	using _Const_thru_ref_type = const _Ty;
};
// 2
template
struct my_remove_reference<_Ty&>
{
	using type = _Ty;
	using _Const_thru_ref_type = const _Ty&;
};
// 3
template
struct my_remove_reference<_Ty&&>
{
	using type = _Ty;
	using _Const_thru_ref_type = const _Ty&&;
};

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

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

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

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

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

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

template
_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
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++中的一个关键字,意思是一个空指针常量。来看看它是怎么定义的:转到头文件

#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
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/883229.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号