decltype与declval

    最近尝试学习decltype和declval,记录一下学习内容

1.decltype是C++11出现的新关键字,在vc中打出来有着高贵的亮蓝色,是用来形容decltype(表达式)中,表达式的类型的。

简单的

int a = 10;
decltype(a) b;

此时b被声明为与a同样的类型,也就是int。

2.declval并不是关键字,只是标准库中的一员,实际上用的时候要用std::declval来使用。而且经常和decltype一起使用来解决decltype的不足,在c++标准文档中给的列子如下:

#include 
#include 
 
struct Default { int foo() const { return 1; } };
 
struct NonDefault
{
    NonDefault() = delete;
    int foo() const { return 1; }
};
 
int main()
{
    decltype(Default().foo()) n1 = 1;                   // n1 的类型是 int
//  decltype(NonDefault().foo()) n2 = n1;               // 错误:无默认构造函数
    decltype(std::declval().foo()) n2 = n1; // n2 的类型是 int
    std::cout << "n1 = " << n1 << '\n'
              << "n2 = " << n2 << '\n';
}

实际上declval在标准库的定义如下:

template
typename std::add_rvalue_reference::type declval() noexcept;

下一部分就会对这展开。

二。declval的定义

在vs2017中找到的定义和标准文档中的写法略有不同但是意义是一样的,在我的type_traits中有如下定义:

	// STRUCT TEMPLATE _Add_reference
template
	struct _Add_reference
	{	// add reference
	using _Lvalue = _Ty;
	using _Rvalue = _Ty;
	};

template
	struct _Add_reference<_Ty, void_t<_Ty&>>
	{	// add reference
	using _Lvalue = _Ty&;
	using _Rvalue = _Ty&&;
	};

	// STRUCT TEMPLATE add_lvalue_reference
template
	struct add_lvalue_reference
	{	// add lvalue reference
	using type = typename _Add_reference<_Ty>::_Lvalue;
	};

template
	using add_lvalue_reference_t = typename _Add_reference<_Ty>::_Lvalue;

	// STRUCT TEMPLATE add_rvalue_reference
template
	struct add_rvalue_reference
	{	// add rvalue reference
	using type = typename _Add_reference<_Ty>::_Rvalue;
	};

template
	using add_rvalue_reference_t = typename _Add_reference<_Ty>::_Rvalue;

	// FUNCTION TEMPLATE declval
template
	add_rvalue_reference_t<_Ty> declval() noexcept;

2.1 void_t

元编程接触的不多,这里对void_t的使用卡住了,于是又查了一下这是干嘛用的。

这是被定义为

template
	using void_t = void;

标准文档的解释是:将任意类型的序列映射到类型 void 的工具元函数。

是c++17中增加的功能,用他可以更方便的实现SFINAE,他就相当于void类型,自己尝试理解,打个比方就如:

template
	using hahha = int;

hahha aaa;

aaa就是一个int类型,跟hahha<>模板里的内容无关,void_t也是如此,只不过是利用void_t<>来尝试进行SFINAE替换操作。

模板参数列表C++11就已经实现了,为什么到C++17才又void_t?

因为之前别名模板中未使用的参数不保证 SFINAE 且可被忽略,但是之前的版本也可以用一些技巧来实现。

这个void_t就理解完了。

 

2.2 一些疑惑

对_Add_reference又进行了一些尝试,

using intl = std::_Add_reference::_Lvalue;
using intr = std::_Add_reference::_Rvalue;
using intnl = std::_Add_reference::_Lvalue;
using intnr = std::_Add_reference::_Rvalue;
using intnnl = std::_Add_reference::_Lvalue;
using intnnr = std::_Add_reference::_Rvalue;

这里 int和int&& 表现出来的结果是一直的 _Lvalue是int&,_Rvalue是int&&

可是int& 确实模板匹配到了

 

template     class = void>
    struct _Add_reference
    {    // add reference
    using _Lvalue = _Ty;
    using _Rvalue = _Ty;
    };

===========================================================

2020.7.3更新 又看了c++标准手册了解到了

这些类型变换遵从折叠规则:

  • std::add_lvalue_reference::type 是 T&
  • std::add_lvalue_reference::type 是 T&
  • std::add_rvalue_reference::type 是 T&
  • std::add_rvalue_reference::type 是 T&&

与直接使用 T& 的主要区别是 std::add_lvalue_reference::type 为 void ,而 void& 导致编译错误。

自己测试也是,确实void& 类型会编译失败!!

 

7.7更新,心中还有一个疑惑就是当类型是T& 是,模板到底是选择的

template
    struct My_Add_reference

还是

template
struct My_Add_reference<_Ty, void_t<_Ty&>>

 

自己有想到了一个新的实验方法:

template
	struct My_Add_reference
{	// add reference
	using _Lvalue = double;
	using _Rvalue = double;
};

template
struct My_Add_reference<_Ty, void_t<_Ty&>>
{	// add reference
	using _Lvalue = _Ty & ;
	using _Rvalue = _Ty && ;
};

我让主模板返回值不再是_Ty类型而是double类型,进行测试

using Cintnl = My_Add_reference::_Lvalue;
using Cintnr = My_Add_reference::_Rvalue;
using Cintnl = My_Add_reference::_Lvalue;
using Cintnr = My_Add_reference::_Rvalue;

可以发现 int& 的 lvalue和rvalue仍然是 int&,而void变成double,说明 int&仍然用的是

template
struct My_Add_reference<_Ty, void_t<_Ty&>> 模板,只不过是因为遵守了叠加规则,导致了左值右值引用都是_Ty&,

主模板的存在只是为了void类型而存在的。

你可能感兴趣的:(c++)