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

时间:2018-11-11 19:21:57

标签: c++ arrays function templates stdvector

我想用一个参数编写一个C ++函数,以便可以传递以下任何一种类型:

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

模板是实现这一目标的最佳方法吗?

5 个答案:

答案 0 :(得分:6)

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

您可以用一个正向迭代器来包装它,这是一种好方法,也就是说,如果您只需要一个一个地迭代一个项目,因为对std::list之类的某些容器来说,随机访问是非常不好的。 / p>

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的更改,它也可以通过直接传递原始数组而不是两个指针来工作(但仍不适用于动态分配的数组)。

答案 1 :(得分:3)

  

模板是实现这一目标的最佳方法吗?

这取决于。如果您要编写的函数包含在标头中,因此可以在以后用于进一步的编译,那么-是的,可能是:

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?

此函数将能够按原样使用几乎所有变量:std::vectorstd::array和普通(大小)数组可以在不使用额外语法的情况下进行传递。但是,对于指针,您将需要调用类似f(gsl::make_span{ptr, numElements})的东西。

PS-标准库中非常常见的第​​三个选项是将插入符而不是容器作为参数。这还需要模板,因此类似于第一个选项。

答案 2 :(得分:2)

不能将所有列出的类型都放在一个功能模板中。但是,您可能会有函数模板重载,这将解决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类型数组。

答案 3 :(得分:1)

span似乎是您想要的。等待C ++ 20 :-)或使用GSL中的span。参见What is a “span” and when should I use one? 。下面的示例。

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

答案 4 :(得分:0)

如果它们都使用int,那么您可以简单地接受开始和结束指针。您可以在其上使用标准的算法,因为 pointers iterators

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

传递两个 iterators (泛型或其他)的好处是您不必将整个容器传递给算法。

相关问题