【问题标题】:stl::iterators with raw pointers带有原始指针的 stl::iterators
【发布时间】:2017-06-17 04:53:33
【问题描述】:

我想将迭代器与 C++ 数组一起使用,但也与原始指针一起使用。 我可以使用静态向量:

#define SIZE 10
int vect[SIZE] = {0};
vect[3] = 5;
int* p = std::find(std::begin(vect), std::end(vect), 5);
bool success = p != std::end(vect);

如何使用原始指针(可能是堆分配的向量)来做到这一点? 当然编译器不知道数据的大小,所以这段代码

int* pStart = vect;
std::find(std::begin(pStart), std::end(pStart), 5);

给予

error C2784: '_Ty *std::begin(_Ty (&)[_Size])' : 
could not deduce template argument for '_Ty (&)[_Size]' from 'int *'

是否可以让begin()end() 意识到这一点?

【问题讨论】:

  • 怎么可能做what?你想达到什么目的?你的目标是什么?
  • 使用std::array 代替,它具有C 数组的所有功能,但实际上具有STL 接口。它的迭代器被实现为指针。
  • @rlbond 除了在这种情况下,普通数组完全可以。

标签: c++ pointers stl


【解决方案1】:

这里:

std::begin(pStart), std::end(pStart)

您正在尝试获取指针的开头和结尾。不!

您的意思是:

std::begin(vect), std::end(vect)

无论您使用数组、std::arraystd::vector 还是特别大的大象,这都是一样的——要获得容器的边界,您需要容器。

【讨论】:

    【解决方案2】:

    是否可以让 begin() 和 end() 意识到这一点?

    对于指针可以实现std::begin,但是实现std::end是不可能的(因为正如你所说,大小是未知的),所以有点没有意义

    但是,您不需要其中任何一个来使用std::find

    int* p = std::find(pStart, pStart + SIZE, 5);
    

    【讨论】:

    • 除了现在你有一个潜在的错误。像这样的手动边界几乎总是一个坏主意。
    【解决方案3】:

    不,不能在指针上使用std::beginstd::end。与大小是类型的一部分并因此可推导出的数组不同,指针不保存它所指向的事物的大小。在您使用指针的情况下,您必须使用

    std::find(pStart, pStart + SIZE, 5);
    

    避免这种情况的方法是在编译时不知道 szie 是什么时使用std::vector。它将为您管理内存并提供beginend 成员函数。

    【讨论】:

    • 使用std::vector 看起来涉及数据的副本。在这种情况下使用 std::find(pStart, pStart + SIZE, 5); 就可以了。
    • @Stefano 我的意思是,如果您使用的是运行时大小,您应该使用 std::vector 开始。如果没有,那么您可以使用原始数组,也可以使用std::array
    • 我使用的是 C++ 数组,因为数据来自共享内存,并且数组的大小是由某种共享内存管理器动态分配的。
    • @Stefano:在这种情况下,共享内存管理器还必须告诉您大小。起始指针和起始指针 + size 迭代器。它们可用于您通过std::beginstd::end 完成的所有操作,尤其是<algorithm> 中的所有标准算法。
    【解决方案4】:

    我想对 C++ 数组使用迭代器,但也使用原始指针。

    你把它弄反了。原始指针迭代器。他们遍历数组。

    您可以将它们用于您通过std::beginstd::end 完成的所有事情。最值得注意的是,您可以将它们传递给 <algorithm> 中的 C++ 标准算法。

    指针本身不能被迭代。迭代器不能被迭代。

    int* pStart = vect;
    std::find(std::begin(pStart), std::end(pStart), 5);
    

    非常松散地说,指针只是一个数字。什么是数字的“开始”和“结束”?这段代码的意义与以下一样:

    int i = 123;
    std::find(std::begin(i), std::end(i), 5); // error
    

    是否可以让begin()end() 意识到这一点?

    指针可能指向某个数组的开头。但是该知识必须与指针一起保存。您需要维护一个大小或一个结束指针以及一个开始指针,并将所有这些数据放在一起。

    这正是 std::arraystd::vector 为您所做的。

    【讨论】:

      【解决方案5】:

      当我处理 naked 数组时,我依靠简单的容器来使它们与 C++ 对 ranges 的一般处理方式兼容。

      例如:

      #include <iostream>
      #include <memory>    // for std::unique_ptr
      #include <algorithm> // for std::reverse
      #include <numeric>   // for std::iota
      
      template<typename T>
      
      class range_view {
      
       public:
      
        range_view(T* data, std::size_t size)
            : m_data { data },
              m_size { size } { }
      
        const T* begin() const { return m_data; }
        const T* end() const { return m_data + m_size; }
      
        T* begin() { return m_data; }
        T* end() { return m_data + m_size; }
      
       private:
      
        T* m_data;
        std::size_t m_size;
      };
      
      int main() {
      
        // this is your actual data
        auto pdata = std::make_unique<int>(20);
      
        // this is a handle you use to work with your data
        auto data_view = range_view<int>(pdata.get(), 20);
      
        // for example, you can fill it with numbers 0, ... , N - 1
        std::iota(data_view.begin(), data_view.end(), 0);
      
        // it is now compatible with c++'s range-based operators
        std::cout << "my data...\n";
        for(auto && item : data_view) std::cout << item << " ";
      
        std::reverse(data_view.begin(), data_view.end());
        std::cout << "\nreversed...\n";
        for(auto && item : data_view) std::cout << item << " ";
        std::cout << "\n";
      }
      

      编译运行

      $ g++ example.cpp -std=c++14
      $ ./a.out
      my data...
      0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 
      reversed...
      19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 
      

      你仍然需要担心将正确的维度传递给构造函数,如果底层指针被删除,它会惨遭失败,但无论如何你不得不担心。

      【讨论】:

        【解决方案6】:

        虽然其他人的回答已经解释了为什么你应该重新审视你的设计,但我想陈述一个选项,但之前没有描述。

        您的编译器诊断消息几乎清楚地表明编译器无法推断参数,并且参数看起来与 С 样式数组的引用完全一样。

        对 C 样式数组的引用不会衰减为指针,因此您可以在特定情况下将 C 样式数组的引用传递给 std::begin 和 std::end 作为选项:

        #include <iostream>
        #include <algorithm>
        
        constexpr size_t SIZE = 10;
        
        int main() {
            int vect[SIZE] = {0};
            vect[3] = 5;
        
            int (&ref_to_vect)[SIZE] = vect;    // declaring reference to C-array
        
                // and using reference to C-array which doesn't decay to pointer opposite to passing array "by value" semantics
            int* p = std::find(std::begin(ref_to_vect), std::end(ref_to_vect), 5);
            bool success = p != std::end(vect);
            std::cout << (success ? "Found 5" : "5 not found :(");
        
            return 0;
        }
        

        【讨论】:

          猜你喜欢
          • 2019-10-03
          • 2011-09-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-06-01
          • 2023-01-08
          • 2017-07-18
          相关资源
          最近更新 更多