C++ 将未知大小的 std::array 传递给函数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17156282/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 20:57:52  来源:igfitidea点击:

Passing a std::array of unknown size to a function

c++c++11stdarray

提问by Adrian

In C++11, how would I go about writing a function (or method) that takes a std::array of known type but unknown size?

在 C++11 中,我将如何编写一个采用已知类型但大小未知的 std::array 的函数(或方法)?

// 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);

During my search I only found suggestions to use templates, but those seems messy (method definitions in header) and excessive for what I'm trying to accomplish.

在我的搜索过程中,我只找到了使用模板的建议,但这些建议看起来很混乱(标题中的方法定义)并且对于我想要完成的事情来说太过分了。

Is there a simple way to make this work, as one would with plain C-style arrays?

有没有一种简单的方法可以使这项工作,就像使用普通的 C 样式数组一样?

采纳答案by Andy Prowl

Is there a simple way to make this work, as one would with plain C-style arrays?

有没有一种简单的方法可以使这项工作,就像使用普通的 C 样式数组一样?

No. You really cannot do that unless you make your function a function template(or use another sort of container, like an std::vector, as suggested in the comments to the question):

不。除非您将函数设置为函数模板(或使用另一种容器,如std::vector问题的评论中建议的 ),否则您真的不能这样做:

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

Here is a live example.

这是一个活生生的例子

回答by Mark B

The size of the arrayis part of the type, so you can't do quite what you want. There are a couple alternatives.

的大小arraytype 的一部分,所以你不能做你想做的。有几种选择。

Preferred would be to take a pair of iterators:

首选是采用一对迭代器:

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

Alternately, use vectorinstead of array, which allows you to store the size at runtime rather than as part of its type:

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

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

回答by musk's

I tried below and it just worked for me.

我在下面尝试过,它对我有用。

#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;
}

OUTPUT :

输出 :

1 2 3 4 5 6 7

1 2 3 4 5 6 7

2 4 6 8 10 12

2 4 6 8 10 12

1 1 1 1 1 1 1 1 1

1 1 1 1 1 1 1 1 1

3 6 9 12 15 18 21

3 6 9 12 15 18 21

10 20 30 40 50 60

10 20 30 40 50 60

2 2 2 2 2 2 2 2 2

2 2 2 2 2 2 2 2 2

回答by suncho

EDIT

编辑

C++20 tentatively includes std::span

C++20 暂定包括 std::span

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

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

Original Answer

原答案

What you want is something like gsl::span, which is available in the Guideline Support Library described in the C++ Core Guidelines:

您想要的是类似于gsl::spanC++ 核心指南中描述的指南支持库中的内容:

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

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

You can find an open-source header-only implementation of the GSL here:

您可以在此处找到 GSL 的开源头文件实现:

https://github.com/Microsoft/GSL

https://github.com/Microsoft/GSL

With gsl::span, you can do this:

使用gsl::span,您可以执行以下操作:

// 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);

The problem with std::arrayis that its size is part of its type, so you'd have to use a template in order to implement a function that takes an std::arrayof arbitrary size.

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

gsl::spanon the other hand stores its size as run-time information. This allows you to use one non-template function to accept an array of arbitrary size. It will also accept other contiguous containers:

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

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

mulArray(vec, 6);
mulArray(carr, 7);

Pretty cool, huh?

很酷吧?

回答by David M. Helmuth

Absolutely, there is a simple way in C++11 to write a function that takes a std::array of known type, but unknown size.

当然,在 C++11 中有一种简单的方法来编写一个函数,该函数采用已知类型但未知大小的 std::array。

If we are unable to pass the array size to the function, then instead, we can pass the memory address of where the array starts along with a 2nd address of where the array ends. Later, inside of the function, we can use these 2 memory addresses to calculate the size of the array!

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

#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;
}

Output at Console:10, 20, 2, 4, 8

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

回答by Yakk - Adam Nevraumont

This can be done, but it takes a few steps to do cleanly. First, write a template classthat represents a range of contiguous values. Then forward a templateversion that knows how big the arrayis to the Implversion that takes this contiguous range.

这可以做到,但需要几个步骤才能干净利落。首先,写一个template class代表一系列连续值的 a。然后将template知道有多大的版本转发arrayImpl采用此连续范围的版本。

Finally, implement the contig_rangeversion. Note that for( int& x: range )works for contig_range, because I implemented begin()and end()and pointers are iterators.

最后,实现contig_range版本。请注意,这for( int& x: range )适用于contig_range,因为我实现了begin()andend()和 指针是迭代器。

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 );
}

(not tested, but design should work).

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

Then, in your .cppfile:

然后,在您的.cpp文件中:

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

This has the downside that the code that loops over the contents of the array doesn't know (at compile time) how big the array is, which could cost optimization. It has the advantage that the implementation doesn't have to be in the header.

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

Be careful about explicitly constructing a contig_range, as if you pass it a setit will assume that the setdata is contiguous, which is false, and do undefined behavior all over the place. The only two stdcontainers that this is guaranteed to work on are vectorand array(and C-style arrays, as it happens!). dequedespite being random access isn't contiguous (dangerously, it is contiguous in small chunks!), listis not even close, and the associative (ordered and unordered) containers are equally non-contiguous.

显式构造 a 时要小心contig_range,就像传递 a 一样,set它会假设set数据是连续的,这是错误的,并在所有地方执行未定义的行为。唯一std保证可以使用的两个容器是vectorand array(和 C 风格的数组,碰巧!)。 deque尽管随机访问不是连续的(危险的是,它是连续的小块!),list甚至不接近,并且关联(有序和无序)容器同样不连续。

So the three constructors I implemented where std::array, std::vectorand C-style arrays, which basically covers the bases.

于是三个构造我实现,其中std::arraystd::vector和C风格的数组,这基本上涵盖了基础。

Implementing []is easy as well, and between for()and []that is most of what you want an arrayfor, isn't it?

实施[]也很容易for()[]这就是您想要的大部分内容array,不是吗?