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
Passing a std::array of unknown size to a function
提问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 array
is part of the type, so you can't do quite what you want. There are a couple alternatives.
的大小array
是type 的一部分,所以你不能做你想做的。有几种选择。
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 vector
instead 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::span
C++ 核心指南中描述的指南支持库中的内容:
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::array
is 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::array
of arbitrary size.
问题std::array
在于它的大小是其类型的一部分,因此您必须使用模板来实现一个具有std::array
任意大小的函数。
gsl::span
on 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 class
that represents a range of contiguous values. Then forward a template
version that knows how big the array
is to the Impl
version that takes this contiguous range.
这可以做到,但需要几个步骤才能干净利落。首先,写一个template class
代表一系列连续值的 a。然后将template
知道有多大的版本转发array
到Impl
采用此连续范围的版本。
Finally, implement the contig_range
version. 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 .cpp
file:
然后,在您的.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 set
it will assume that the set
data is contiguous, which is false, and do undefined behavior all over the place. The only two std
containers that this is guaranteed to work on are vector
and array
(and C-style arrays, as it happens!). deque
despite being random access isn't contiguous (dangerously, it is contiguous in small chunks!), list
is not even close, and the associative (ordered and unordered) containers are equally non-contiguous.
显式构造 a 时要小心contig_range
,就像传递 a 一样,set
它会假设set
数据是连续的,这是错误的,并在所有地方执行未定义的行为。唯一std
保证可以使用的两个容器是vector
and array
(和 C 风格的数组,碰巧!)。 deque
尽管随机访问不是连续的(危险的是,它是连续的小块!),list
甚至不接近,并且关联(有序和无序)容器同样不连续。
So the three constructors I implemented where std::array
, std::vector
and C-style arrays, which basically covers the bases.
于是三个构造我实现,其中std::array
,std::vector
和C风格的数组,这基本上涵盖了基础。
Implementing []
is easy as well, and between for()
and []
that is most of what you want an array
for, isn't it?
实施[]
也很容易for()
,[]
这就是您想要的大部分内容array
,不是吗?