【问题标题】:Returning two dimensional array of strings返回字符串的二维数组
【发布时间】:2014-04-21 05:34:01
【问题描述】:
string** flowFile() {
    string line;
    string word[8];
    int i=0;
    static string flow[23][2];
    ifstream myfile ("test.txt");
    if (myfile.is_open())
    {
       while ( getline (myfile,line) )
        {
             strSplit(line,word);
             flow[i][0]=word[1];
             flow[i++][1]=word[2];
        }
        myfile.close();
   }
   else cout << "Unable to open file"; 
   return flow;
 }

 int main()
  {
     string **fl=flowFile();
  }

我收到此错误:

error: cannot convert ‘std::string (*)[2] {aka std::basic_string<char> (*)[2]}’
                to    ‘std::string** {aka std::basic_string<char>**}’
       in return

我的代码有什么问题?

【问题讨论】:

    标签: c++ string


    【解决方案1】:

    string flow[23][2]string ** 是两种不同的不兼容类型。一个人不能隐式转换为另一个人。就这样。解决方案是通过使后面的string [23][2] 返回引用和接受引用来使它们兼容,但这仍然是一个糟糕的解决方案,因为您仍在使用原始数组。

    一个好的解决方案是使用std::vectorstd::string。也许,你还需要std::pair,或者std::array

    这是一种可能的解决方案:

    #include <vector>
    #include <array>
    #include <string>
    
    //C++11 style typedef
    using flow_data_t = std::vector<std::array<std::string,2>>; 
    
    //reimplementation of your function
    flow_data_t flowFile() 
    {
        std::string line;
        std::string word[8];
        int i=0;
        flow_data_t flow;
        std::ifstream myfile ("test.txt");
        if ( !myfile )  
           cout << "Unable to open file"; 
        while ( std::getline (myfile,line) )
        {
           strSplit(line,word);
           flow.push_back({word[0], word[1]});
        }
        return flow;
    }
    
    int main()
    {
      flow_data_t data=flowFile();
    
      for(auto const & row : data)
          for(auto const & col : row)
                //work!
    }
    

    希望对您有所帮助。

    【讨论】:

    • by making the later string (*)[2] 你是怎么做到的?
    • @texasbruce:通过这样写:string (*fl)[2]=flowFile(); 并同样更改函数的返回类型。
    • @Nawaz 你试过函数返回类型到string(*)[2]吗?它对你有用吗?
    • @texasbruce:请尝试一下。如果 typedef 让你的生活变得地狱,请使用它(因为我知道语法看起来像地狱)。
    • 如果 OP 想要一个二维数组,难道不是 vector> 或 map,string> 更合适吗?
    【解决方案2】:

    即使你可以返回一个指针并让你的数组衰减为一个指针,你也不能从函数中返回数组:Array Decay

    然而二维数组不能衰减到T*T**,因为数组的memory layout不同于“二维指针数组”(实际上更像是扁平化的),并且你不能从函数返回数组.但是在 C++ 中,您可以返回数组引用 Full Code:

    //This does not work
    //typedef string * string2d[2];
    //typedef string *(&string2d)[2];
    
    typedef string (&string2d)[23][2];
    
    string2d flowFile() {
        static string flow[23][2];
       return flow;
    }
    

    数组引用甚至会保留每行和每列有多大的信息,并且不会发生数组衰减。

    当然,更建议的“C++ 方式”是使用std::vector(一如既往)。

    【讨论】:

      【解决方案3】:
      1. 在 C++ 中,数组的类型为 std::vector。您应该使用这些,而不是使用 [] 声明的低级内置数组。
      2. 在 C++ 中,string [23] 有时可以与string* 互换,但string[23][2] 永远不能与string** 互换。这就是你不应该使用内置数组的原因之一。
      3. 在 C++ 中,不能返回本地内置数组变量。它会编译,但你的程序可能会崩溃。这是您不应该使用内置数组的另一个原因。 (虽然返回一个 static 数组应该没问题)。
      4. 还有很多原因。

      【讨论】:

        【解决方案4】:

        返回指向静态变量的指针并没有错。只是必须正确声明返回类型。如果您尝试重现声明的含义以及编译器相应地尝试执行的操作,那是有道理的。考虑声明static string flow[23][2];。它声明了 23 行字符串,每行有 2 个元素。如果您将其视为一维数组,它会有所帮助。数组元素恰好是数组,但现在这并不重要(但我们会回到它)。从这个角度来看,数组只有 23 个元素,每个元素的大小为 2 个字符串。与所有数组一样,元素(这里:2 个字符串的数组)只是在内存中排列。

        像任何数组一样,flow 在大多数情况下都会衰减为指向其第一个元素的指针。递增该指针将指向下一个元素,即第二行。在数值上,编译器必须将 2*sizeof(string) 添加到 flow 的地址,以便计算 flow 的下一个元素的地址,即 flow[1]。 (它直接在 flow[0] 后面。这里没有魔法。)

        现在如果你声明string **flowpp,flowpp 已经是一个指针,不需要衰减。如果我们认为它指向数组中的第一个元素,那么元素的类型是什么?果然:纯指针。递增 flowpp 将让它指向下一个元素。我的指针有 4 个字节大,因此将 just 4 数字添加到 flowpp 就足以访问 flowpp 的下一个元素。与需要添加到流中的内容相比(请记住,2*sizeof(string)),这是完全不同的。 编译器根据指针指向的内容计算元素的偏移量!这两种情况有很大不同。

        那么可以你的函数返回什么?当你返回它时,流量会衰减到什么程度?它衰减为指向其第一个元素的指针。元素是两个字符串的数组。它必须是 string xxx[2],其中 xxx 是一个指针:因此是 string (*p)[2]。如果指针实际上是由函数返回的,我们有一个函数调用而不是普通的 p,所以它是(*f())[2]

        这是一个完整的例子:

        #include<iostream>
        using namespace std;
        
        const int numFlowElems = 3, numArrElems = 2;
        
        /** @return a pointer to the first element of a static array
            of string[numArrElems]s.
        */  
        string (*flowFile())[numArrElems]
        {   // init so that we see something below.
            static string flow[numFlowElems][numArrElems] 
                        = {{"1","2"}, 
                           {"3","4"},
                           {"5","6"}
                          };
        
            // your function code ...
            return flow;
        }
        
        int main()
        {
             // array decays to ptr, like usual. Ptr elems are string[numArrElems].
             // ptrToArr is a pointer to arrays of two strings.
             string (*ptrToArr)[numArrElems] = flowFile();
        
             for( int flowInd= 0; flowInd<numFlowElems; ++flowInd )
             {
                for(int strInd = 0; strInd<numArrElems; ++strInd)
                {
                    cout << ptrToArr[flowInd][strInd] << ' ';       
                }
                cout << endl;
             }
        
             return 0;
        }
        

        你如何解析string (*flowFile())[numArrElems]?我需要两次尝试才能使声明正确,如果这是任何安慰的话。关键是在 C 和 C++ 中(不是在 C# 中,请注意!)声明具有表达式的形状。

        您可以从内到外进行:flowFile() 是一个函数。结果被取消引用,因为函数调用的优先级高于星号:*flowFile() 是取消引用的结果。显然,结果是一个大小为 numArrElems 的数组,其中的元素是字符串。

        你可以在外面做:(*flowFile())[numArrElems] 的结果被声明为一个字符串。 (*flowFile()) 是一个包含 numArrElems 元素的字符串数组。显然,flowFile() 必须取消引用才能获得该数组,以便 flowfile 是一个返回指向 numArrElems 字符串数组的指针的函数。确实如此!它返回 flow 的第一个元素,也就是一个字符串数组。

        向量的向量可能确实更容易;如果你想保留语义,你应该传递引用,正如其他人提到的那样:毕竟,原始程序中的所有函数都将在同一个静态数组上运行。如果您按值传递向量,情况将不再如此。但是,这实际上可能是有益的。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-05-02
          • 2018-06-10
          • 2020-07-16
          • 2016-04-04
          • 2016-05-06
          • 2021-06-12
          • 1970-01-01
          • 2019-01-23
          相关资源
          最近更新 更多