代码之家  ›  专栏  ›  技术社区  ›  Jabberwocky

将不同大小的std::数组传递给函数[duplicate]

c++
  •  0
  • Jabberwocky  · 技术社区  · 6 年前

    在C++ 11中,我将如何编写一个函数(或方法),它需要一个已知类型但未知大小的STD::

    // made up example
    void mulArray(std::array<int, ?>& arr, const int multiplier) {
        for(auto& e : arr) {
            e *= multiplier;
        }
    }
    
    // lets imagine these being full of numbers
    std::array<int, 17> arr1;
    std::array<int, 6>  arr2;
    std::array<int, 95> arr3;
    
    mulArray(arr1, 3);
    mulArray(arr2, 5);
    mulArray(arr3, 2);
    

    在我的搜索过程中,我只找到了使用模板的建议,但是这些建议看起来很混乱(header中的方法定义),而且对于我想要完成的任务来说太多了。

    有没有一种简单的方法可以实现这一点,就像使用普通的c样式数组一样?

    0 回复  |  直到 10 年前
        1
  •  71
  •   Andy Prowl    11 年前

    有没有一种简单的方法可以实现这一点,就像使用普通的c样式数组一样?

    不,除非你把你的函数变成函数 模板 (或者使用另一种容器,比如 std::vector ,如对问题的评论中所建议的那样):

    template<std::size_t SIZE>
    void mulArray(std::array<int, SIZE>& arr, const int multiplier) {
        for(auto& e : arr) {
            e *= multiplier;
        }
    }
    

    这是一个 live example 是的。

        2
  •  24
  •   Mark B    11 年前

    的大小 array 部分类型 ,所以你不能做你想做的事。有两种选择。

    最好是使用一对迭代器:

    template <typename Iter>
    void mulArray(Iter first, Iter last, const int multiplier) {
        for(; first != last; ++first) {
            *first *= multiplier;
        }
    }
    

    或者,使用 vector 而不是数组,它允许您在运行时存储大小,而不是作为其类型的一部分:

    void mulArray(std::vector<int>& arr, const int multiplier) {
        for(auto& e : arr) {
            e *= multiplier;
        }
    }
    
        3
  •  5
  •   eyllanesc Yonghwan Shin    6 年前

    我在下面试了一下,结果就成功了。

    #include <iostream>
    #include <array>
    
    using namespace std;
    
    // made up example
    void mulArray(auto &arr, const int multiplier) 
    {
        for(auto& e : arr) 
        {
            e *= multiplier;
        }
    }
    
    void dispArray(auto &arr)
    {
        for(auto& e : arr) 
        {
            std::cout << e << " ";
        }
        std::cout << endl;
    }
    
    int main()
    {
    
        // lets imagine these being full of numbers
        std::array<int, 7> arr1 = {1, 2, 3, 4, 5, 6, 7};
        std::array<int, 6> arr2 = {2, 4, 6, 8, 10, 12};
        std::array<int, 9> arr3 = {1, 1, 1, 1, 1, 1, 1, 1, 1};
    
        dispArray(arr1);
        dispArray(arr2);
        dispArray(arr3);
    
        mulArray(arr1, 3);
        mulArray(arr2, 5);
        mulArray(arr3, 2);
    
        dispArray(arr1);
        dispArray(arr2);
        dispArray(arr3);
    
        return 0;
    }
    

    输出:

    1 2 3 4 5 6 7

    2 4 6 8 10 12年

    1 1 1 1 1 1 1 1 1 1 1

    3 6 9 12 15 18 21

    10 20 30 40 50 60

    2 2 2 2 2 2 2 2 2 2 2

        4
  •  2
  •   David M. Helmuth gerardw    6 年前

    绝对地,C++ 11中有一个简单的方法来编写一个函数,该函数接收已知类型的STD数组:但未知大小。

    如果无法将数组大小传递给函数,则可以传递数组开始位置的内存地址以及数组结束位置的第二个地址。稍后,在函数内部,我们可以使用这两个内存地址来计算数组的大小!

    #include <iostream>
    #include <array>
    
    // The function that can take a std::array of any size!
    void mulArray(int* piStart, int* piLast, int multiplier){
    
         // Calculate the size of the array (how many values it holds)
         unsigned int uiArraySize = piLast - piStart;
    
         // print each value held in the array
         for (unsigned int uiCount = 0; uiCount < uiArraySize; uiCount++)     
              std::cout << *(piStart + uiCount) * multiplier << std::endl;
    }
    
    int main(){   
    
         // initialize an array that can can hold 5 values
         std::array<int, 5> iValues;
    
         iValues[0] = 5;
         iValues[1] = 10;
         iValues[2] = 1;
         iValues[3] = 2;
         iValues[4] = 4;
    
         // Provide a pointer to both the beginning and end addresses of 
         // the array.
         mulArray(iValues.begin(), iValues.end(), 2);
    
         return 0;
    }
    

    控制台输出: 10,20,2,4,8

        5
  •  2
  •   suncho    6 年前

    编辑

    C++ 20初步包括 std::span

    https://en.cppreference.com/w/cpp/container/span

    原始答案

    你想要的是 gsl::span 这在C++核心指南中描述的指南支持库中是可用的:

    https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#SS-views

    您可以在这里找到gsl的一个开放源代码的header-only实现:

    https://github.com/Microsoft/GSL

    GSL::跨度 ,您可以执行以下操作:

    // made up example
    void mulArray(gsl::span<int>& arr, const int multiplier) {
        for(auto& e : arr) {
            e *= multiplier;
        }
    }
    
    // lets imagine these being full of numbers
    std::array<int, 17> arr1;
    std::array<int, 6>  arr2;
    std::array<int, 95> arr3;
    
    mulArray(arr1, 3);
    mulArray(arr2, 5);
    mulArray(arr3, 2);
    

    问题在于 std::array 它的大小是其类型的一部分,因此必须使用模板来实现一个 std::数组 任意大小的。

    GSL::跨度 另一方面,将其大小存储为运行时信息。这允许您使用一个非模板函数来接受任意大小的数组。它还将接受其他相邻的容器:

    std::vector<int> vec = {1, 2, 3, 4};
    int carr[] = {5, 6, 7, 8};
    
    mulArray(vec, 6);
    mulArray(carr, 7);
    

    很酷吧?

        6
  •  1
  •   Yakk - Adam Nevraumont    11 年前

    这是可以做到的,但要做到干净需要几步。首先,写一个 template class 表示连续值范围的。然后转发 template 版本知道有多大 array 是为了 Impl 接受此连续范围的版本。

    最后,实现 contig_range 版本。请注意 for( int& x: range ) 为…工作 连续范围 ,因为我实现了 begin() end() 指针是迭代器。

    template<typename T>
    struct contig_range {
      T* _begin, _end;
      contig_range( T* b, T* e ):_begin(b), _end(e) {}
      T const* begin() const { return _begin; }
      T const* end() const { return _end; }
      T* begin() { return _begin; }
      T* end() { return _end; }
      contig_range( contig_range const& ) = default;
      contig_range( contig_range && ) = default;
      contig_range():_begin(nullptr), _end(nullptr) {}
    
      // maybe block `operator=`?  contig_range follows reference semantics
      // and there really isn't a run time safe `operator=` for reference semantics on
      // a range when the RHS is of unknown width...
      // I guess I could make it follow pointer semantics and rebase?  Dunno
      // this being tricky, I am tempted to =delete operator=
    
      template<typename T, std::size_t N>
      contig_range( std::array<T, N>& arr ): _begin(&*std::begin(arr)), _end(&*std::end(arr)) {}
      template<typename T, std::size_t N>
      contig_range( T(&arr)[N] ): _begin(&*std::begin(arr)), _end(&*std::end(arr)) {}
      template<typename T, typename A>
      contig_range( std::vector<T, A>& arr ): _begin(&*std::begin(arr)), _end(&*std::end(arr)) {}
    };
    
    void mulArrayImpl( contig_range<int> arr, const int multiplier );
    
    template<std::size_t N>
    void mulArray( std::array<int, N>& arr, const int multiplier ) {
      mulArrayImpl( contig_range<int>(arr), multiplier );
    }
    

    (未经测试,但设计应该有效)。

    然后,在你的 .cpp 文件:

    void mulArrayImpl(contig_range<int> rng, const int multiplier) {
      for(auto& e : rng) {
        e *= multiplier;
      }
    }
    

    这样做的缺点是循环遍历数组内容的代码(在编译时)不知道数组有多大,这可能会导致优化成本。它的优点是实现不必在头中。

    在显式构造 连续范围 ,好像你通过了 set 它将假定 设置 数据是连续的,这是错误的,并且在所有地方都执行未定义的行为。只有两个 std 保证能在上面工作的容器是 vector 阵列 (和c样式的数组,事实上!)是的。 deque 尽管随机访问是不连续的(危险的是,它在小块中是连续的!)我是说, list 甚至不接近,并且关联(有序和无序)容器同样是不连续的。

    所以我实现的三个构造函数 std::array ,请 std::vector 和c样式的数组,基本上覆盖了基。

    实施 [] 也很容易,而且 for() [] 这就是你想要的 阵列 因为,不是吗?