如何编写可以接受数组或向量的函数?

How to write a function that can take in an array or a vector?

我想编写一个带有一个参数的 C++ 函数,以便可以传入以下任何一种类型:

std::vector<int>
std::array<int>
int array[numElements]
int *ptr = new int[numElements]
etc

模板化是完成此任务的最佳方式吗?

如果你希望只能做 func(v) 你做不到,因为我无法想到你的函数可以推断出动态分配的大小 int[numElements].

包装它的一个好方法是采用一对前向迭代器,也就是说,如果您只需要一个接一个地迭代项目,因为随机访问在某些容器上非常糟糕,例如 std::list .

template<class FWIt>
void func(FWIt a, const FWIt b)
{
    while (a != b)
    {
        std::cout << "Value: " << *a << '\n';
        ++a;
    }
}

template<class T>
void func(const T& container)
{
    using std::begin;
    using std::end;
    func(begin(container), end(container));
}

这适用于以下情况:

int array[5] = {1, 2, 3, 4, 5};
func(array);

int* dynarray = new int[5]{1, 2, 3, 4, 5};
func(dynarray, dynarray + 5);

std::vector<int> vec{1, 2, 3, 4, 5};
func(vec);
func(vec.begin(), vec.end());

std::list<int> list{1, 2, 3, 4, 5};
func(list);

编辑: 由于@DanielH 的更改,这也可以通过直接传递原始数组而不是作为两个指针来工作(但仍然不适用于动态分配的数组)。

您无法将所有列出的类型添加到一个函数模板中。但是,您可以重载函数模板,这将解决 std::vector<> 的问题, std::array<>Type array[numElements].

template<typename Iter>
void funArray(const Iter begin, const Iter end) 
{
    std::cout << "Actual code here\n";
}

template<typename Container> void funArray(const Container& arr)
{
    funArray(std::begin(arr), std::end(arr)); //std::vector or std::array
}

现在你可以写:

int main()
{
    const std::size_t numElements = 5;
    std::vector<int> vec;
    std::array<int, numElements> arr;
    int array[numElements];
    int *ptr = new int[numElements];

    funArray(vec);
    funArray(arr);
    funArray(array);
    funArray(ptr, ptr+numElements); 
    return 0;
}

但是,对于动态分配的数组,你需要按照用户@Asu的建议使用。


编辑:删除了多余的重载。

template<typename T, std::size_t N> void funArray(const T (&arr)[N]) {}

正如@Daniel H指出的那样,上面的C类型数组函数模板重载是徒劳的,因为它可以由第二个重载处理(template<typename Container>), 通过直接推导到 C 类型数组。

Would templating be the best way to accomplish this?

视情况而定。如果您正在编写一个进入 header 的函数,因此可以在以后用于进一步编译,那么 - 是的,可能:

template <typename IntContainer>
void f(Container& c);

template <typename IntContainer>
void f(const Container& c);

但是,如果只编译一次实现,您应该考虑:

void f(gsl::span<int> sp);

void f(gsl::span<const int> sp);

使用跨度。如果您还没有听说过它们,请阅读:

What is a "span" and when should I use one?

此函数将能够获取几乎所有变量 as-is:std::vectorstd::array 和普通(大小)数组无需额外语法即可传递.但是,对于指针,您需要调用类似 f(gsl::make_span{ptr, numElements}).

的方法

PS - 第三种选择,在标准库中很常见,是将交互器而不是容器作为参数。这也需要模板,所以它类似于第一个选项。

span 似乎是您要查找的内容。等待 C++20 :-) 或使用 GSL 中的 span。请参阅 什么是“跨度”,我应该在什么时候使用它? 。示例如下。

#include <array>
#include <iostream>
#include <vector>

#if __cplusplus > 201709L
#include <span>
using std::span;
#else
#include <gsl/gsl>
using gsl::span;
#endif

void func(span<int> data){
    for(auto i : data){
        std::cout << i << ' ';
    }
    std::cout <<'\n';
}

int main(){
    std::vector<int> stdvec(3);
    func(stdvec);
    std::array<int,3> stdarr;
    func(stdarr);
    int carr[3];
    func(carr);
    int *ptr = new int[3]();
    func({ptr,3});
    delete []ptr;
    return EXIT_SUCCESS;
}

如果他们都使用 int 那么你可以简单地接受开始和结束指针。您可以对它们使用标准的算法,因为指针迭代器

void my_func(int const* begin, int const* end)
{
    std::for_each(begin, end, [](int i){
        std::cout << i << '\n';
    });
}

然后:

std::vector<int> v;
std::array<int> a;
int array[numElements];
int* ptr = new int[numElements];


my_func(v.data(), v.data() + v.size());

my_func(a.data(), a.data() + a.size());

my_func(std::begin(array), std::end(array));

my_func(ptr, ptr + numElements);

传递两个 迭代器(通用或其他)的好处是您不必将整个容器传递给算法。