【问题标题】:C++ Type and Value Category for Expression and Variable表达式和变量的 C++ 类型和值类别
【发布时间】:2016-04-01 06:21:02
【问题描述】:

从这个link,它说

对象、引用、函数(包括函数模板特化)和表达式都有一个名为 type 的属性

所以给出以下内容:

int &&rf_int = 10;

我可以说变量rf_int 是复合类型对 int 的右值引用

但是在谈到value category时,它特别说

每个表达式都有一些非引用类型

每个 C++ 表达式(带有操作数的运算符、文字、变量名称等)

基于以上两条语句,rf_int 可以被视为一个表达式,并且表达式具有非引用类型

现在我真的很困惑。 rf_int 是否有 reference 类型?在谈论名称的类型时,我们是否必须提供上下文,无论是变量还是表达式?

更具体地说,当在函数调用中使用变量名时:

SomeFunc(rf_int);

现在将rf_int 视为表达式(因此它是 int 类型的左值)还是变量(因此它是类型为 int 的右值引用的左值)?

编辑here 的评论让我对这个问题感到好奇。

【问题讨论】:

  • 术语变量在[basic]p6中定义如下:“变量是由非静态数据成员以外的引用的声明引入的或对象。变量的名称(如果有)表示引用或对象。" 据我了解,name 也没有类型,但对象或引用它所指的有一个类型。比较 decltype(rf_int)decltype((rf_int))
  • @dyp decltype(rf_int)decltype(*entity*)decltype((rf_int))decltype(*expression*),根据decltype
  • 是的。这就是我的观点:实体rf_int 具有与表达式rf_int 不同的(可观察的)类型。或者更确切地说,对于表达式,在进一步分析之前删除了引用。因此,虽然从技术上讲,表达式rf_int 具有int&& 类型,但对于任何可观察的目的,它具有int 类型和值类别lvalue

标签: c++


【解决方案1】:

rf_int 是否有引用类型?

名称为rf_int实体(或变量)因其声明方式而具有类型int&&(引用类型),但表达式 rf_int 根据[expr]/5 具有int 类型(非引用类型):

如果一个表达式最初的类型是“引用T”([dcl.ref], [dcl.init.ref]), 类型调整为T 之前的任何进一步 分析。表达式指定由表示的对象或函数 引用,并且表达式是左值或 xvalue,具体取决于 表达式上。 [ 注意: 在引用的生命周期之前 已开始或结束后,行为未定义(请参阅 [基本生活])。 ——尾注]


在谈论名称的类型时,我们是否必须提供上下文? 是变量还是表达式?

是的,我们有。 rf_int可以说是有不同的类型,这取决于它是指实体还是表达式。


更具体地说,当在函数调用中使用变量名时:

SomeFunc(rf_int);

rf_int 现在是否被视为一个表达式(因此它是一个左值 类型int),或一个变量(因此它是一个右值类型的左值 参考int)?

它被认为是一个表达式,它是一个int 类型的左值。 (注意,值类别是表达式的属性。说变量是左值是不正确的。)

【讨论】:

    【解决方案2】:

    首先让我感到困惑,但让我以一种简单的方式消除歧义。

    EXPRESSION 是可以评估且必须评估为非引用类型的东西,对吗?
    (是的,当然是啊!!)

    现在我们也知道变量名是一个左值表达式
    (老兄已经说到点子上了,别再把表达式加粗了)

    好的,现在要注意了,当我们说变量时,我们指的是内存中的一个位置。现在我们将内存中的一个地方称为表达式吗?不,这绝对不是完全荒谬的。

    表达式是一个通用术语,我们通过定义一些规则来识别,任何符合这些规则的东西都是一个表达式。有必要以这种方式定义它,以便在编译器构造期间理解代码。该规则之一是任何计算为值的东西都是表达式。由于从编码的角度来看,使用变量名意味着您希望在编译代码时使用实际值,因此我们将该变量名称为表达式。

    因此,当他们说变量是表达式时,他们并不是指内存中的变量,而是从编码的角度来看变量 NAME。但是使用术语“变量名”来区分实际变量(在内存中的位置)是荒谬的。所以说“变量是一个表达式”只要你从编码的角度去想就可以了。

    现在先回答这个问题:

    更具体地说,当在函数调用中使用变量名时:

    SomeFunc(rf_int);
    

    现在rf_int 是否被视为一个表达式(因此它是一个左值 类型int),或一个变量(因此它是一个右值类型的左值 参考int)?

    单个变量也是一个表达式。所以这个问题就失效了。

    现在回答这个问题:

    基于以上两条语句,rf_int 可以被视为一个 表达式和表达式具有非引用类型。

    现在我真的很困惑。 rf_int 有没有引用类型?

    如果我说 rf_int 是一个右值引用,rf_int 也是一个左值 表达式
    (哦,兄弟,这家伙和他的痴迷带表达式)

    这是真的,因为如果您执行以下操作,它将起作用。

    int &&rf_int = 10;   // rf_int is an r-value reference 
    int &x = rf_int;     // x is an l-value reference and l-value reference can be initialized with l-value expression
    cout << x;           //Output will be 10
    

    现在rf_int 是一个表达式还是一个右值引用,它会是什么?答案是两者兼而有之。这取决于您从哪个角度思考。

    换句话说,我想说的是,如果我们认为 rf_int 是一个变量(内存中的某个位置),那么它肯定具有 r 值引用的类型,但因为 rf_int 也是一个变量名称,从编码的角度来看,它是一个表达式,更准确地说是一个左值表达式,每当你使用这个变量进行评估时,你都会得到值10,这是一个int,所以我们一定会说@作为表达式的 987654333@ 类型是 int ,它是非引用类型。

    如果您从编译器的角度思考一下,哪一行代码会计算为引用?没有对吗?您可以尝试搜索,如果您发现任何也让我知道。但这里的重点是表达式的类型并不意味着变量的类型。它表示计算表达式后得到的值的类型。

    希望我已经澄清了你的问题。如果我错过了什么,请告诉我。

    【讨论】:

      【解决方案3】:

      基于this,每个函数调用都是一个表达式。 传递给函数的每个参数也是一个表达式。

      因此,当您调用 SumFunc(rf_int); 时,您会从单个 rf_int 变量创建一个表达式。

      ISO/IEC 14882(c++14 标准)规定“表达式指定引用表示的对象或函数,表达式是左值或 x 值,具体取决于表达式。” em>

      因此,rf_int 表达式将是 int 类型。

      This comment(你之前提到过)是关于类型转换错误的。

      为了说明我的解释,我准备了一个更复杂但(希望)更易于理解的示例。 这个例子是关于为什么我们需要右值引用类型以及如何正确使用它。

      class Obj {
          int* pvalue;
      public:
          Obj(int m) {
              pvalue = new int[100];
              for(int i = 0; i < 100; i++)
                  pvalue[i] = m + i;
          }
          Obj(Obj& o) {  // copy constructor
              pvalue = new int[100];
              for(int i = 0; i < 100; i++)
                  pvalue[i] = o.pvalue[i];
          }
          Obj(Obj&& o) {  // move constructor
              for(int i = 0; i < 100; i++)
                  pvalue = o.pvalue;
              o.pvalue = nullptr;
          }
         // ...
      };
      

      示例 1

        Obj obj1(3);
        Obj obj2(obj1);  // copy constructor
        Obj obj3(std::move(obj1)); // move constructor
        Obj obj4(Obj(3)); // move constructor
      

      第 1 行 - 创建了 obj1。
      第 2 行 - 作为 obj1 的副本创建的 obj2
      第 3 行 - 通过将 obj1 的值移动到 obj3 创建的 obj3; obj1 失去它的价值
      第 4 行 - Obj(3) 创建的临时对象;通过将临时对象的值移动到 obj4 来创建 obj4;临时对象失去了它的价值

      我认为我们不需要int&amp;&amp;,但Obj&amp;&amp; 可能非常有用。尤其是在第 4 行。

      示例 2 我对编译器错误的(简化)解释

      void SomeFunc0(Obj   arg) {};
      void SomeFunc1(Obj&  arg) {};
      void SomeFunc2(Obj&& arg) {};
      
      int main()
      {
        Obj   obj1(3);        // object
        Obj&  obj2 = obj1;    // reference to the object
        Obj&& obj3 = Obj(3);  // reference to the temporary object with extened lifetime
      
      
          SomeFunc0(obj1); // ok - new object created from Obj
          SomeFunc0(obj2); // ok - new object created from Obj&
          SomeFunc0(obj3); // ok - new object created from Obj&&
          SomeFunc0(Obj(3)); // ok - new object created from temporary object
          SomeFunc0(std::move(obj1)); // ok - new object created from temporary object
          SomeFunc0(std::move(obj2)); // ok - new object created from temporary object
          SomeFunc0(std::move(obj3)); // ok - new object created from temporary object
      
          SomeFunc1(obj1); // ok - reference to obj1 passed
          SomeFunc1(obj2); // ok - reference to obj1 passed
          SomeFunc1(obj3); // ok - reference to temp. obj. passed
          SomeFunc1(Obj(3)); // error - lifetime of the temp. obj. too short
          SomeFunc1(std::move(obj1)); // error - lifetime of the temp. obj. too short
          SomeFunc1(std::move(obj2)); // error - lifetime of the temp. obj. too short
          SomeFunc1(std::move(obj3)); // error - lifetime of the temp. obj. too short
      
          SomeFunc2(obj1); // error - temporary object required
          SomeFunc2(obj2); // error - temporary object required
          SomeFunc2(obj3); // error - lifetime of the temp. obj. too long
          SomeFunc2(Obj(3)); // ok
          SomeFunc2(std::move(obj1)); // ok
          SomeFunc2(std::move(obj2)); // ok
          SomeFunc2(std::move(obj3)); // ok
      
          return 0;
      }
      

      【讨论】:

      • @VijayChavda 谢谢你的评论。我更新了我的答案以更具体
      猜你喜欢
      • 2011-09-09
      • 1970-01-01
      • 1970-01-01
      • 2013-09-18
      • 2020-03-14
      • 2014-02-20
      • 1970-01-01
      • 2011-12-18
      相关资源
      最近更新 更多