将未知大小的 std::数组传递给函数
Passing a std::array of unknown size to a function
在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);
在我的搜索过程中,我只找到了使用模板的建议,但这些建议似乎很混乱(标题中的方法定义(并且对于我想要完成的任务来说过多。
有没有一种简单的方法可以像使用普通 C 样式数组一样使这项工作
有没有一种简单的方法可以像使用普通 C 样式数组一样使这项工作
?
不。除非你把你的函数变成一个函数模板(或者使用另一种容器,如std::vector
,如问题注释中所建议的(,否则你真的不能这样做:
template<std::size_t SIZE>
void mulArray(std::array<int, SIZE>& arr, const int multiplier) {
for(auto& e : arr) {
e *= multiplier;
}
}
这是一个活生生的例子。
array
的大小是类型的一部分,因此您无法完全按照自己的方式进行操作。有几种选择。
首选是采用一对迭代器:
template <typename Iter>
void mulArray(Iter first, Iter last, const int multiplier) {
for(; first != last; ++first) {
*first *= multiplier;
}
}
或者,使用 vector
而不是 array,这允许您在运行时存储大小,而不是作为其类型的一部分:
void mulArray(std::vector<int>& arr, const int multiplier) {
for(auto& e : arr) {
e *= multiplier;
}
}
编辑
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 的开源标头实现:
https://github.com/Microsoft/GSL
使用 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);
std::array
的问题在于它的大小是其类型的一部分,因此您必须使用模板才能实现采用任意大小std::array
的函数。
另一方面,gsl::span
将其大小存储为运行时信息。 这允许您使用一个非模板函数来接受任意大小的数组。 它还将接受其他连续的容器:
std::vector<int> vec = {1, 2, 3, 4};
int carr[] = {5, 6, 7, 8};
mulArray(vec, 6);
mulArray(carr, 7);
很酷吧?
我在下面尝试过,它对我有用。
#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
3 6 9 12 15 18 21
10 20 30 40 50 60
2 2 2 2 2 2 2 2 2
当然,C++11 中有一种简单的方法来编写一个函数,该函数采用已知类型但大小未知的 std::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{ 5, 10, 1, 2, 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
这是可以做到的,但需要几个步骤才能干净利落地完成。 首先,编写一个表示一系列连续值的template class
。 然后将知道array
有多大的template
版本转发到采用此连续范围的Impl
版本。
最后,实现contig_range
版本。 请注意,for( int& x: range )
适用于contig_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;
}
}
这样做的缺点是,循环遍历数组内容的代码不知道(在编译时(数组有多大,这可能会优化成本。 它的优点是实现不必在标头中。
在显式构造contig_range
时要小心,就像你给它传递一个set
它会假设set
数据是连续的,这是错误的,并且到处都是未定义的行为。 唯一保证可以处理的两个std
容器是 vector
和 array
(以及 C 样式数组,因为它碰巧! deque
尽管是随机访问并不是连续的(危险的是,它是小块连续的!(,list
甚至不接近,并且关联(有序和无序(容器同样不连续。
所以我实现的三个构造函数std::array
、std::vector
和 C 风格的数组,基本上涵盖了基础。
实现[]
也很容易,在for()
和[]
之间,这是您想要array
的大部分内容,不是吗?
- C++中的高效循环缓冲区,它将被传递给C样式数组函数参数
- 打印 2D 动态数组 c++ 函数
- C++数组函数
- 在数组函数指针中调用void
- 数组函数似乎没有找到最大值
- 使用数组函数和循环求解伪元编码|C
- 在头文件中的函数原型中获取指针数组:函数声明为 void
- 数组函数错误输出
- 读取文件,然后创建数组函数C++
- 指针数组函数参数-奇怪的行为
- 二维数组 - 函数
- "return"没有从数组函数返回正确的值
- 数组函数问题
- 数组函数的大小返回 int 的大小而不是数组
- 从数组函数中删除元素
- 如何从C++中最好地调用具有C99风格数组函数签名的函数
- 追加/打印/插入数组c++函数问题
- 如何将数组函数转换为指针函数
- 多维数组函数输出垃圾
- C++:数组函数的递归最大值