首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使用带有rvalue引用param //的初始化器来使用另一个C样式数组变量初始化C样式数组

如何使用带有rvalue引用param //的初始化器来使用另一个C样式数组变量初始化C样式数组
EN

Stack Overflow用户
提问于 2017-12-07 13:16:40
回答 2查看 970关注 0票数 4

序言

在阅读这篇文章之前,请注意我是C++初学者。我还没有学会所有的(基本的)概念(例如模板),但我正在尝试完全理解一些基本面,然后再去做其他的事情。

因此,请不要提到std::string或std:array、vectors、boost::数组,以及它们相对于C样式数组的优越性。我相信他们是这样的,但这并不是重点:)

现在,与使用其成员初始化程序列表相比,有时更倾向于在ctor主体中分配元素。

我的问题

考虑下面的例子。当我必须定义一个复制构造函数来初始化main()中声明的character2时,我注意到在Visual 2017中,IntelliSense显示了3个不同的重载来从初始化程序列表中初始化类成员。例如,对于“name”变量,可用的列出的重载如下:

  1. char [50](const char [50] &)
  2. char [50](char [50] &&)
  3. char [50]()

附属问题1:为什么IntelliSense允许仅在变量声明为类成员时才显示这些重载?(如果CTRL+SHIFT+SPACE是在main()中声明的,则不会显示任何内容)

最让我感兴趣的是其中有参考价值的那个:

  • “name”变量的char [50](char [50] &&)
  • “data”变量的int [10](int [10] &&)
  • “单数据”变量的int(int &&)

现在,如果在初始化程序列表中使用name(),则似乎使用了重载#3。如果我使用name("aaa"),则似乎使用了重载#1。这让我研究了其他概念:我现在理解了lvalue、rvalue和lvalue引用的概念(但不是,现在对我来说太复杂了),而且我没有完全理解rvalue引用和移动语义的概念。

考虑到我的部分理解,我试图在我的“参数化构造函数2”中声明一个rvalue引用(然后它变成类型为"rvalue引用int“的lvalue ),并使用它初始化'singledata‘变量,希望看到初始化器重载#3在IntelliSense中弹出,但它没有。在这种情况下,带有lvalue引用参数(重载#2)的那个似乎再次被使用。我无法解释。

在这里,我被困住了,这是我的主要问题1:带rvalue引用参数的初始化器什么时候恰好被用于类成员?

主要问题2:为什么不能使用另一个char数组初始化char数组,而可以使用字符串文字表达式?因为存在纯xvalue (Prvalue),而变量最多只能转换为xvalue表达式?

--我知道这绝对不是标准的,但是如果我查看反汇编,字符数组的初始化非常简单:

代码语言:javascript
复制
     2:     const char arrchar1[10]("hello");
01142771 A1 38 9B 14 01       mov         eax,dword ptr [string "hello" (01149B38h)]  
01142776 89 45 D8             mov         dword ptr [arrchar1],eax  
01142779 66 8B 0D 3C 9B 14 01 mov         cx,word ptr ds:[1149B3Ch]  
01142780 66 89 4D DC          mov         word ptr [ebp-24h],cx  
01142784 33 C0                xor         eax,eax  
01142786 89 45 DE             mov         dword ptr [ebp-22h],eax 

当前堆栈框架中的堆栈分配空间[arrchar1] (在我的例子中是[ebp-28h] )只是由一个MOV指令集填充(实际上,在本例中:1 dword被移动为“地狱”,然后一个单词被移动到其余的),其中包含"hello“字符串的静态空间的内容。

为什么在编译后没有类似的C++方法来执行相同的操作,而是“移动”堆栈分配的空间(另一个数组变量)的内容,而不是“移动”一些静态内存?我预计会发生这样的事情:char arrchar2[10](arrchar1)

示例:

代码语言:javascript
复制
#include <string.h> // for strcpy

class Character
{
public:
    //default constructor
    Character() {};

    //parameterized constructor 1
    Character(const char * pname) : // pointer to the string literal must be const (otherwise it is Undefined Behaviour)
        name(), data{ 1, 2, 3 } // mem-initializer-list: 
                                // - name is initialized with value-initialization (empty expression-list in a pair of parentheses following identifier)
                                // - (C++11) data is initialized using list-initialization which becomes aggregate-initialization since it is an aggregate (array)
    {
        strcpy_s(name, pname);
    };

    //parameterized constructor 2
    Character(const char * pname, int &&val1) :
        name(), data{ 1, 2, 3 }, singledata(val1)
    {
        strcpy_s(name, pname);
    };

    //copy constructor
    Character(const Character & tocopy)
        // member initializer list

        //:name(),  // >> IntelliSense shows 3 initializer overloads: char [50](const char [50] &) || char [50](char [50] &&) || char [50]()
                    // >> (visible only if the array is declared in a class, no pop up in main...)

        : name("aaa"),

        //data()    // >> IntelliSense shows 3 initializer overloads: int [10](const int [10] &) || int [10](int [10] &&) || int [10]()
                    // >> (visible only if the array is declared in a class, no pop up in main...)

        data{ 1, 2, 3 }
    {
        // ctor body definition
    };
private:
    char name[50];
    int data[10];
    int singledata;
};

void main()
{

    Character character1("characterOne"); // the string literal has static storage duration (static memory), passed pointer allocated on stack memory
    Character character2(character1);
    Character character3("characterThree", 3);
}
EN

回答 2

Stack Overflow用户

发布于 2017-12-07 13:49:39

问题1:

我相信您是在问为什么您的singledata初始化没有调用它的r值初始化器。简短的回答:因为你没有传递给它一个r值。

在上面的构造函数中,参数val1具有r值引用类型.但是,请记住,“r值”属性适用于表达式而不是类型。在初始化singledata(val1)中,子表达式val1是l值,即使变量val1具有r值引用类型。它是由使某物成为l值的规则所定义的l值.顺便说一句,这些规则实际上只是一系列条件,如果满足这些条件,表达式就会变成l-值。不过,粗略地说,您可以在这里解释val1是一个l-值,因为它是一个可以获取地址的对象。

如果您想要singledata初始化器的r值引用版本,则应该使用singledata(std::move(val1))。这里,表达式std::move(val1)有一个r值类型,通过定义std::move所做的事情。因此,singledata构造函数中的r值引用可以绑定到它。

票数 1
EN

Stack Overflow用户

发布于 2017-12-15 14:55:24

对附属问题1:的答复

因为作为结构或类的一部分的数组不会衰减(例如,当整个结构或类被传递给一个函数时)。

来源:http://www.learncpp.com/cpp-tutorial/6-8-pointers-and-arrays/

对主要问题1:的答复

正如斯米希指出的那样,正确的方法似乎是在成员初始化程序列表中使用单数据(std::move(Val1))。我认为Visual (2017)只是没有显示正确的重载,如下所示:string文字的移动-哪个编译器是正确的?

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/47696006

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档