【问题标题】:How can I pass a part of a vector as a function argument?如何将向量的一部分作为函数参数传递?
【发布时间】:2013-06-03 17:10:57
【问题描述】:

我在 C++ 程序中使用 vector,我需要将其中的一部分 vector 传递给函数。

如果是 C,我需要执行以下操作(使用数组):

int arr[5] = {1, 2, 3, 4, 5};
func(arr+2);  // Pass the part of the array {3, 4, 5}

除了用最后一部分创建一个新的vector之外,还有其他方法吗?

【问题讨论】:

  • "如果是 c,我需要这样做(使用数组):" 这将假设 func 知道它需要一个包含 3 个元素(或更少)。如果它需要一个 5 的数组,那你就完蛋了。
  • 您很可能应该完全做其他事情。你为什么要传递原始数据?向量代表什么?我会封装它并对其执行操作,而不是在其他地方传递它。

标签: c++ vector containers parameter-passing partial


【解决方案1】:

C++20 开始,我将使用Ranges library 中的范围,因为它提供了多种范围适配器,用于在矢量上创建不同的视图。对于您的用例,我将使用范围适配器std::views::drop,如下所示:

int main() {
    std::vector<int> arr {1, 2, 3, 4, 5};

    // Ignore the first two elements and pass only {3, 4, 5} to func().
    func(arr | std::views::drop(2));

    return 0;
}

这样您就不必为迭代器或指针/迭代器算术而烦恼。 此外,不会为您缩短的arr 创建临时向量,因为视图适配器drop() 创建了一个不包含元素的范围。结果范围只是原始向量arr 的视图,但具有自定义的迭代行为。

对于func() 的声明,我将使用占位符类型auto 作为函数参数,因为结果范围的类型非常复杂。这使得func() 成为function template,不过:

void func(auto range) {
    for (int i : range)
        std::cout << i << std::endl;
}

(或者,您可以通过引用func() 来传递arr 并在func() 中应用范围适配器。)

输出:

3
4
5

Code on Wandbox

【讨论】:

    【解决方案2】:

    最新的 (C++20) 方法是使用 std::span 创建一个 std::span查看 std::vector 的一部分并将其传递给职能。注意:元素在内存中必须是连续的,才能在容器上使用std::span,以及std::vector is continuous in memory

    #include <span>
    
    std::vector<int> int_vector = {1, 2, 3, 4, 5};
    std::span<int> a_span(int_vector.data() + 2, int_vector.size() - 2);
    for(const int a : a_span);
    for(const int& a : a_span);
    function(a_span);
    

    【讨论】:

      【解决方案3】:

      我也遇到了同样的问题。我发现了一个非常好的技巧。假设您想找到arrLR(包括两者)范围内的最小值,那么您可以执行以下操作:

      vector<int>arr = {4,5,1,3,7};
      
      int minVal = *min_element(begin(arr)+L,begin(arr)+(R+1));
      

      意味着你传递了完整的数组和范围,然后你就可以应用上面的技巧了。

      【讨论】:

        【解决方案4】:

        一种常见的方法是传递迭代器范围。这适用于所有类型的范围,包括那些属于标准库容器和普通数组的范围:

        template <typename Iterator>
        void func(Iterator start, Iterator end) 
        {
          for (Iterator it = start; it !=end; ++it)
          {
             // do something
          } 
        }
        

        然后

        std::vector<int> v = ...;
        func(v.begin()+2, v.end());
        
        int arr[5] = {1, 2, 3, 4, 5};
        func(arr+2, arr+5);
        

        注意:虽然该函数适用于所有类型的范围,但并非所有迭代器类型都支持通过operator+v.begin()+2 中使用的增量。如需其他选择,请查看std::advancestd::next

        【讨论】:

        • 我想说的更广泛一点:该函数适用于所有范围。容器和数组是管理范围的一种方式,但它们不是唯一的方式。
        【解决方案5】:

        正如其他一些人已经说过的那样,您可以为此使用迭代器。您必须将序列的开头和结尾传递给您的工作函数。

        如果您需要更大的灵活性,您应该查看slice。例如,使用slice,您可以检索向量的每个第 n 个条目。

        【讨论】:

          【解决方案6】:

          通常您可以发送迭代器。

          static const int n[] = {1,2,3,4,5};
          vector <int> vec;
          copy (n, n + (sizeof (n) / sizeof (n[0])), back_inserter (vec));
          
          vector <int>::iterator itStart = vec.begin();
          ++itStart; // points to `2`
          vector <int>::iterator itEnd = itStart;
          advance (itEnd,2); // points to 4
          
          func (itStart, itEnd);
          

          这不仅适用于vectors。但是,由于 vector 保证了连续存储,只要 vector 不重新分配,您就可以发送元素的地址:

          func (&vec[1], &vec[3]);
          

          【讨论】:

            【解决方案7】:
            std::vector<char> b(100); 
            send(z,&b[0],b.size(),0);
            

            试试这个。

            Read this too.

            【讨论】:

            • 你的意思是&amp;b[2],不是吗? &amp;b[0] 产生错误的结果。
            • 这对向量很有效,因为它们是连续的。 stackoverflow.com/questions/849168/…。请注意不要使用不连续的存储容器尝试这种类型的东西。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2014-08-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多