首页 > 其他分享 >引用在模板推导中的基础逻辑

引用在模板推导中的基础逻辑

时间:2023-05-12 19:44:31浏览次数:40  
标签:ref 推导 tree TREE 引用 TYPE type 模板

reference

引用是C++相对于C语言指针引入的一个新语法,可以以简单变量来使用指针。这种语法在使用的时候还是比较方便的,但是也在模板类型推导的过程中也带来了一些需要额外关注的细节。

例子

下面的例子中,rt是一个引用类型,问题是在模板参数函数Harry的定义中,模板参数TSECER并没有包含引用类型,那么此时以rt为参数调用时,模板函数中的Fry参数是什么类型呢,或者更详细的说:引用会被传递给模板吗?

tsecer@harry: cat cpp.tmpl.type.deduction.cpp 
struct tsecer
{};

template<typename TSECER>
void Harry(const TSECER Fry)
{}

void Leela()
{
    tsecer t, &rt = t, *pt = &t;
    Harry(rt);
    Harry(pt);
}

资料

在网络上有关于该问题的一个讨论,答案也是简单明了:如果调用的地方是一个引用表达式,那么此时引用会先被剥离。

If an expression initially has the type “reference to T” ([dcl.ref], [dcl.init.ref]), the type is adjusted to T.

该答案引用了c++语法中的规定

If an expression initially has the type “reference to T” ([dcl.ref], [dcl.init.ref]), the type is adjusted to T prior to any further analysis. The expression designates the object or function denoted by the reference, and the expression is an lvalue or an xvalue, depending on the expression.
[Note 1: Before the lifetime of the reference has started or after it has ended, the behavior is undefined (see [basic.life]). — end note]

大致的意思是,在调用

Harry(rt);

时,此时rt是一个引用类型的表达式(expression),C++规定引用类型表达式在推导的时候会先去掉引用,所以rt作为参数的时候,匹配的argument是单独的tsecer类型—没有引用。所以,此时调用的时候会生成一个临时变量给Harry函数调用时使用,对应的模板函数也是单独的const TSECER类型而没有引用。

模板

前面说调用处类型是引用的话会先去掉引用,对应地,在模板参数定义中的引用也会做相应的处理,这个相对前一个规则来说是一个比较明确的位置。文档中这么说明

If P is a reference type, the referenced type is used for deduction.

这个感觉和调用处的说明相呼应:调用处表达式的引用会被剔除,而模板parameter中也只是使用引用的类型来进行匹配,这样其实两个部分是对应/对等的。或者说:引用不作为类型属性来进行模板匹配

这里再补充说明下,前面说的都是参数推导而不是参数替换(substitute)。当推导完成、参数替换之后,模板定义中就会有引用出现了。以下面代码为例

struct tsecer
{};

template<typename TSECER>
void Harry(const TSECER &Fry)
{}

void Leela()
{
    tsecer t, &rt = t;
    Harry(rt);
}

在调用处,rt先剥夺引用类型,成为简单的tsecer类型;在模板函数中,先去掉const(如果有volatile也会被剥离),然后去掉引用,此时参数中就是一个简单的TSECER类型;这样模板的parameter TSECER和调用处的argument tsecer直接简单匹配,推导出此次调用的模板parameter就是tsecer。最后将这个类型带入到Harry函数(参数替换),Harry就是一个const tsecer &类型为参数的函数了。

gcc

argument

/* In all nodes that are expressions, this is the data type of the expression.
   In POINTER_TYPE nodes, this is the type that the pointer points to.
   In ARRAY_TYPE nodes, this is the type of the elements.
   In VECTOR_TYPE nodes, this is the type of the elements.  */
#define TREE_TYPE(NODE) \
(CONTAINS_STRUCT_CHECK (NODE, TS_TYPED)->typed.type)

/* Like is_bitfield_with_lowered_type, except that if EXP is not a
   bitfield with a lowered type, the type of EXP is returned, rather
   than NULL_TREE.  */

tree
unlowered_expr_type (const_tree exp)
{
  tree type;
  tree etype = TREE_TYPE (exp);

  type = is_bitfield_expr_with_lowered_type (exp);
  if (type)
    type = cp_build_qualified_type (type, cp_type_quals (etype));
  else
    type = etype;

  return type;
}

对于引用类型,在gcc内部是

/* C unary `*' or Pascal `^'.  One operand, an expression for a pointer.  */
DEFTREECODE (INDIRECT_REF, "indirect_ref", tcc_reference, 1)

类型结构,也就是引用在gcc内部就是 ptr这种形式表达了,只是在语言层面屏蔽了指针的dereference。或者说,Harry(rt)和Harry(pt)在gcc看来是一样的。

在访问引用类型标识符时,编译器会自动把标识符转换为间接引用类型

/* We are using a reference VAL for its value. Bash that reference all the
   way down to its lowest form.  */

tree
convert_from_reference (tree val)
{
  if (TREE_TYPE (val)
      && TREE_CODE (TREE_TYPE (val)) == REFERENCE_TYPE)
    {
      tree t = TREE_TYPE (TREE_TYPE (val));
      tree ref = build1 (INDIRECT_REF, t, val);

      mark_exp_read (val);
       /* We *must* set TREE_READONLY when dereferencing a pointer to const,
	  so that we get the proper error message if the result is used
	  to assign to.  Also, &* is supposed to be a no-op.  */
      TREE_READONLY (ref) = CP_TYPE_CONST_P (t);
      TREE_THIS_VOLATILE (ref) = CP_TYPE_VOLATILE_P (t);
      TREE_SIDE_EFFECTS (ref)
	= (TREE_THIS_VOLATILE (ref) || TREE_SIDE_EFFECTS (val));
      val = ref;
    }

  return val;
}

而在build1_stat函数中,也是把引用的类型赋值给了TREE_TYPE,对应代码

TREE_TYPE (t) = type;

也就是说,unlowered_expr_type对于变量引用也同样返回的引用的类型。

tree
build1_stat (enum tree_code code, tree type, tree node MEM_STAT_DECL)
{
  int length = sizeof (struct tree_exp);
  tree t;

  record_node_allocation_statistics (code, length);

  gcc_assert (TREE_CODE_LENGTH (code) == 1);

  t = ggc_alloc_tree_node_stat (length PASS_MEM_STAT);

  memset (t, 0, sizeof (struct tree_common));

  TREE_SET_CODE (t, code);

  TREE_TYPE (t) = type;
///...
}

parameter

/* Adjust types before performing type deduction, as described in
   [temp.deduct.call] and [temp.deduct.conv].  The rules in these two
   sections are symmetric.  PARM is the type of a function parameter
   or the return type of the conversion function.  ARG is the type of
   the argument passed to the call, or the type of the value
   initialized with the result of the conversion function.
   ARG_EXPR is the original argument expression, which may be null.  */

static int
maybe_adjust_types_for_deduction (unification_kind_t strict,
				  tree* parm,
				  tree* arg,
				  tree arg_expr)
{
///...
  /* [temp.deduct.call]

     If P is a cv-qualified type, the top level cv-qualifiers
     of P's type are ignored for type deduction.  If P is a
     reference type, the type referred to by P is used for
     type deduction.  */
  *parm = TYPE_MAIN_VARIANT (*parm);
  if (TREE_CODE (*parm) == REFERENCE_TYPE)
    {
      *parm = TREE_TYPE (*parm);
      result |= UNIFY_ALLOW_OUTER_MORE_CV_QUAL;
    }
///...
}

栗子

前面说的都是“顶层”类型说明,在非顶层结构中,依然是要严格匹配const、reference等类型的。

在下面的例子中,foo函数参数有引用,所以能够匹配成功;而bar函数的int没有引用,导致匹配失败。

tsecer@harry: cat -n  ref.in.func.arg.cpp    
     1  int foo(int &);
     2  int bar(int);
     3
     4  template<typename T>
     5  void baz(int(*)(T &))
     6  {
     7      T t;
     8  }
     9
    10  int tsecer()
    11  {
    12      baz(foo);
    13      baz(bar);
    14  }
    15
tsecer@harry: gcc -c ref.in.func.arg.cpp  
ref.in.func.arg.cpp: In function 'int tsecer()':
ref.in.func.arg.cpp:13:12: error: no matching function for call to 'baz(int (&)(int))'
     baz(bar);
            ^
ref.in.func.arg.cpp:5:6: note: candidate: template<class T> void baz(int (*)(T&))
 void baz(int(*)(T &))
      ^~~
ref.in.func.arg.cpp:5:6: note:   template argument deduction/substitution failed:
ref.in.func.arg.cpp:13:12: note:   mismatched types 'T&' and 'int'
     baz(bar);
            ^
tsecer@harry: 

标签:ref,推导,tree,TREE,引用,TYPE,type,模板
From: https://www.cnblogs.com/tsecer/p/17396140.html

相关文章

  • CF1824D LuoTianyi and the Function & 区间历史和模板
    LuoTianyiandtheFunction:LuoTianyigivesyouanarray\(a\)of\(n\)integersandtheindexbeginsfrom\(1\).Define\(g(i,j)\)asfollows:When\(i\lej\),\(g(i,j)\)isthelargestinteger\(x\)thatsatisfies\(\{a_p:i\lep\le......
  • C++ 模板
     模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。模板是创建泛型类或函数的蓝图或公式。库容器,比如迭代器和算法,都是泛型编程的例子,它们都使用了模板的概念。每个容器都有一个单一的定义,比如 向量 ,我们可以定义许多不同类型的向量,比如 vector<in......
  • C++ 模板
    模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。模板是创建泛型类或函数的蓝图或公式。库容器,比如迭代器和算法,都是泛型编程的例子,它们都使用了模板的概念。每个容器都有一个单一的定义,比如 向量 ,我们可以定义许多不同类型的向量,比如 vector<int> 或......
  • 基恩士KV7500,KV8000轴控制FB模板,直接可以拿来用,使基恩士编程也随心所欲。
    基恩士KV7500,KV8000轴控制FB模板,直接可以拿来用,使基恩士编程也随心所欲。包含了适配5种定位控制单元的FB,像常用的KV-XH16ML、KV-SH04PL等都有适配的FB。功能上包含了原点返回、绝对定位、相对定位、速度控制、力矩控制、两轴直线插补等。可以说常用的基本都有了,非常方便使用。重......
  • 三菱,FX3U,plc程序模板和触摸屏程序模板,适用于运动轴控制,程序可以在自动的时候暂停进行
    三菱,FX3U,plc程序模板和触摸屏程序模板,适用于运动轴控制,程序可以在自动的时候暂停进行手动控制,适用于一些中大型设备,可以防止某个气缸超时时,处于自动模式,能够轻松处理,处理完成后,恢复原来的气缸,解除暂停即可,思路清晰,编程效率大大提高,程序里附带和仪表的无协议通讯,并且附带最常用的手......
  • 西门子界面官方精美触摸屏+WINCC程序模板 西门子官方触摸屏程序模板,炫酷的扁平式动画
    西门子界面官方精美触摸屏+WINCC程序模板西门子官方触摸屏程序模板,炫酷的扁平式动画效果,脚本动画,自动生成二维码,可仿真,堪比智能手机,有精简,精致,wincc,无线面板等包含了所有西门子人机界面。YID:4639656292012492......
  • c# 模板方法
    abstractclassAbstractClass{publicvoidTemplateMethod(){PrimitiveOperation1();PrimitiveOperation2();}protectedabstractvoidPrimitiveOperation1();protectedabstractvoidP......
  • SIEMENS/西门子1214 PID/通信模板 西门子PLC 1214和
    SIEMENS/西门子1214PID/通信模板西门子PLC1214和多台G120西门子变频器ModbudRTU通讯。(1)西门子触摸屏;(2)变频器参数/Modbus通讯报文详细讲解;(3)PID自写FB块无密码可直接应用到程序;(4)PID带手动自动功能,可手动调节PID;(5)程序注释详细(6)完整的CAD电气图纸。程序块已经在很多个项目上成熟应......
  • #Mitsubishi/三菱 PLC张力控制通用程序模板 采用三菱伺服F
    #Mitsubishi/三菱PLC张力控制通用程序模板采用三菱伺服FX3U的速度与力矩模式,收料采用锥度与恒张力两种控制模式。程序包含(1)锥度计算详细步骤(2)模拟量输入输出(3)张力检测,有PID调节.(4)完整的电气图纸是锂电行业分切机通用程序模板。对于做三菱PLC(张力控制)朋友有很好的借鉴意义。所有......
  • SIEMENS/西门子水处理1200PLC程序模板 (1)西门子触摸屏程
    SIEMENS/西门子水处理1200PLC程序模板(1)西门子触摸屏程序,博图V16学习程序,可仿真实验(2)Plc程序包含功能块,功能块为SCL编写,(要有一定基础才能读懂)(3)含模拟量换算功能块(4)滤波功能块(5)时间换算功能块(6)modubustcp通讯程序(7)完整的电气原理图(8)完整的操作说明书程序块已经在很多个项目上成熟应......