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

Effective Modern C++[实践]->理解decltype

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

Effective Modern C++[实践]->理解decltype

decltype关键字
  • 何谓decltype
  • 推导规则说明:
    • 规则1
    • 规则2
    • 综合示例:
  • 应用
    • 应用1
    • 应用2
    • 书中示例研习
      • 缺陷代码1
      • 缺陷代码2
      • c++14的最终版本如下

何谓decltype

decltype 是“declare type”的缩写,译为“声明类型”。
decltype说明符是c++ 11引入的另一个新特性。decltype检查实体的声明类型,或表达式的类型和值类别。
语法如下:

decltype ( 实体 ) 	  (1) 	  (C++11 起)
decltype ( 表达式 ) 	  (2) 	  (C++11 起)
推导规则说明: 规则1
  1. 如果实参是没有括号的标识表达式或没有括号的类成员访问表达式,那么 decltype 产生以该表达式命名的实体的类型。如果没有这种实体或该实参指名了一组重载函数,那么编译错误。
  • 示例1
#include 
int main() {
	int x = 42;
    std::vector v(100, x); // v 是一个 vector
}

生成的代码如下

#include 
int main(){
  int x = 42;
  std::vector v = std::vector >(100, x, std::allocator());
  return 0;
}
  • 示例2
struct S {
    int x = 42;
};
const S s;
decltype(s.x) y; // Y的类型是int,尽管s.x是const

生成的代码如下

 struct S{
  int x;
  // inline constexpr S() noexcept = default;
};
int main(){
  const S s = S();
  int y;
  return 0;
}
  • 示例3
int func(){
    return 0;
}
int func(int a){
    return 0;
}
int main(){
    int i = 4;
    // 不正确的使用。Func为重载函数命名
    decltype(func) var1;
    // 正确的使用。重载操作没有歧义
    decltype(func(i)) var2;
    return 0;
}
规则2
  1. 如果实参是其他类型为 T 的任何表达式,且
    1. 如果 表达式 的值类别是亡值,将会 decltype 产生 T&&;
    2. 如果 表达式 的值类别是左值,将会 decltype 产生 T&;
    3. 如果 表达式 的值类别是纯右值,将会 decltype 产生 T。该类型不需要是完整类型或拥有可用的析构函数,而且类型可以是抽象的。此规则不适用于其子表达式:decltype(f(g())) 中,g() 必须有完整类型,但 f() 不必。

注意:如果对象的名字带有括号,那么它会被当做通常的左值表达式,从而 decltype(x) 和 decltype((x)) 通常是不同的类型。在难以或不可能以标准写法进行声明的类型时,decltype 很有用,例如 lambda 相关类型或依赖于模板形参的类型。
示例1

int f() { return 42; }
int& g() { static int x = 42; return x; }

int main() {
	int x = 42;
	decltype(f()) a = f(); // a的类型是int
	decltype(g()) b = g(); // b的类型是int&
	decltype((x)) c = x;   // c的类型是int&,因为x是左值
}

生成的代码如下:

int f(){ return 42;}
int & g(){
  static int x = 42;
  return x;
}
int main(){
  int x = 42;
  int a = f();
  int & b = g();
  int & c = x;
  return 0;
}
综合示例:
#include 
#include 
#include 

using namespace std;

struct A { double x; };

int&& f1(void);  //返回值为 int&&

int main() {
    int var = 1;
    
    const A *a = new A();
    decltype(f1()) a1 = 0 ;
	decltype(var) a2 ;
	decltype(a->x) a3;
    decltype((a->x)) a4 =11;
}

编译生成代码如下:

#include 
#include 
#include 

using namespace std;

struct A
{
  double x;
};
int && f1();
  //返回值为 int&&

int main()
{
  int var = 1;
  const A * a = new A();
  
  int && a1 = 0;//decltype(f1()) a1 = 0 ;
  int a2;//decltype(var) a2 ;
  double a3;//decltype(a->x) a3;
  const double & a4 = 11;//decltype((a->x)) a4 =11;
  return 0;
}
decltype类型说明
decltype(f1()) a1const int&& 一个指向const int对象的右值引用。
decltype(var);int 变量var的类型。
decltype(a->x); double成员访问的类型。
decltype((a->x)) a4const double&内部圆括号导致语句被作为表达式而不是成员访问进行计算。由于a被声明为const指针,该类型是const double类型的引用。
应用 应用1

c++11中,比较典型的应用示例是decltype与typeusing的合用。

using size_t = decltype(sizeof(0));
using ptrdiff_t = decltype((int*)0-(int*)0);
using nullptr_t = decltype(nullptr);
应用2
  1. c++11
template
auto add(T&& t, U&& u) -> decltype(t+u) { return t + u; }

int main() {
	int x = 42;
  	double y = 77.22;
  	add(x,y);
}

生成代码如下:

template
auto add(T&& t, U&& u) -> decltype(t+u) { return t + u; }


#ifdef INSIGHTS_USE_TEMPLATE
template<>
double add(int & t, double & u)
{
  return static_cast(t) + u;
}
#endif
int main()
{
  int x = 42;
  double y = 77.219999999999999;
  add(x, y);
  return 0;
}
  1. c++14
template
decltype(auto) add1(T&& t, U&& u){ return t + u; }

int main() {
	int x = 42;
  	double y = 77.22;
  	add1(x,y);
}

生成的代码如下:

template
decltype(auto) add1(T&& t, U&& u){ return t + u; }

#ifdef INSIGHTS_USE_TEMPLATE
template<>
double add1(int & t, double & u){
  return static_cast(t) + u;
}
#endif
int main(){
  int x = 42;
  double y = 77.219999999999999;
  add1(x, y);
  return 0;
}
书中示例研习 缺陷代码1
template
auto authAndAcess(T &t,U u){
	return t[u];
}

此代码调用authAndAcess(d,5)后推导代码如下:

template<>
int authAndAcess >, int>(std::deque > & t, int u)
{
  return t.operator[](u);
}

原因为:auto关键字进行类别推导时会忽略引用特性,因此虽然d[5]返回的时int &,但是最终却是int的结果。因此无法执行authAndAcess(d,5)=10

缺陷代码2
//推导为int &,但是无法接收右值容器
template
decltype(auto) authAndAcess1(T &t,U u){
	return t[u];
}

此代码调用authAndAcess(d,5)后推导代码如下:

template<>
int & authAndAcess1 >, int>(std::deque > & t, int u)
{
  return t.operator[](u);
}

可以实现赋值了,但是形参第一个无法接收右值

c++14的最终版本如下
#include 
#include 
using namespace std;
//推导为int ,无法对返回值赋值
template
auto authAndAcess(T &t,U u){
	return t[u];
}
//推导为int &,但是无法接收右值容器
template
decltype(auto) authAndAcess1(T &t,U u){
	return t[u];
}
//c++ 14的最终版本
template
decltype(auto) 
authAndAcess2(T &&t,U u){
	return std::forward(t)[u];
}

std::deque makeStringDeque(){
	return {"1","2","3","4","5"};
}
int main() {
	std::deque d={0,1,2,3,4,5,6,7};
  
  	authAndAcess(d,5);
  	authAndAcess1(d,5)=10;
    //note: candidate function [with T = std::deque>, U = int] not viable: expects an lvalue for 1st argument
  	//authAndAcess1(makeStringDeque(),2);//第一个参数是左值,不能传入右值
  	auto s = authAndAcess2(makeStringDeque(),5);
  	authAndAcess2(d,5)= 12;
  	//std::deque makeStringDeque();
}

编译后代码生成的代码如下:

#include 
#include 
using namespace std;
//推导为int ,无法对返回值赋值
template
auto authAndAcess(T &t,U u){
	return t[u];
}

#ifdef INSIGHTS_USE_TEMPLATE
template<>
int authAndAcess >, int>(std::deque > & t, int u)
{
  return t.operator[](u);
}
#endif

//推导为int &,但是无法接收右值容器
template
decltype(auto) authAndAcess1(T &t,U u){
	return t[u];
}

#ifdef INSIGHTS_USE_TEMPLATE
template<>
int & authAndAcess1 >, int>(std::deque > & t, int u)
{
  return t.operator[](u);
}
#endif

//c++ 14的最终版本
template
decltype(auto) authAndAcess2(T &&t,U u){
	return std::forward(t)[u];
}

#ifdef INSIGHTS_USE_TEMPLATE
template<>
std::basic_string & authAndAcess2, std::allocator > >, int>(std::deque, std::allocator > > && t, int u)
{
  return std::forward, std::allocator > > >(t).operator[](static_cast(u));
}
#endif


#ifdef INSIGHTS_USE_TEMPLATE
template<>
int & authAndAcess2 > &, int>(std::deque > & t, int u)
{
  return std::forward > &>(t).operator[](static_cast(u));
}
#endif


std::deque, std::allocator > > makeStringDeque()
{
  return std::deque, std::allocator > >{std::initializer_list >{std::basic_string(std::basic_string("1", std::allocator())), std::basic_string(std::basic_string("2", std::allocator())), std::basic_string(std::basic_string("3", std::allocator())), std::basic_string(std::basic_string("4", std::allocator())), std::basic_string(std::basic_string("5", std::allocator()))}, std::allocator >()};
}

int main()
{
  std::deque d = std::deque >{std::initializer_list{0, 1, 2, 3, 4, 5, 6, 7}, std::allocator()};
  authAndAcess(d, 5);
  authAndAcess1(d, 5) = 10;
  std::basic_string s = std::basic_string(authAndAcess2(makeStringDeque(), 5));
  authAndAcess2(d, 5) = 12;
  return 0;
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/993926.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

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

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