Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Template function taking a std::vector or std::array

I have a function that currently accepts 2 vectors that can contain any plain old data ...

template <class T>
void addData(const vector<T>& yData, vector<T> xData)
{ .. }

Question:

  • Would it be possible to modify it to take two std::array or two std::vector, or even a combination thereof, given that these containers take a different number of template arguments?
like image 615
learnvst Avatar asked Dec 21 '12 00:12

learnvst


People also ask

What is the difference between std :: array and std :: vector?

Difference between std::vector and std::array in C++ Vector is a sequential container to store elements and not index based. Array stores a fixed-size sequential collection of elements of the same type and it is index based. Vector is dynamic in nature so, size increases with insertion of elements.

Is a template a vector?

vector is a template class, which can be instantiated with a type, in the format: vector<int> , vector<double> , vector<string> . The same template class can be used to handle many types, instead of repeatably writing codes for each of the type.

Should you use std :: array?

You should not notice any difference in runtime performance while you still get to enjoy the extra features. Using std::array instead of int[] style arrays is a good idea if you have C++11 or boost at hand.

What is the point of std :: array?

std::array is merely a wrapper around the C-style fixed arrays. to provide type-safety with useful interfaces. Stack-allocation implies that the data for the array is stored in the object itself.


3 Answers

Sure, it's just a matter of creating a suitable type trait. The example just uses a function f() with one argument but it is trivial to extend to take any number of arguments.

#include <array>
#include <vector>
#include <deque>
#include <utility>
#include <cstddef>

template <typename T>
struct is_array_or_vector {
    enum { value = false };
};

template <typename T, typename A>
struct is_array_or_vector<std::vector<T, A>> {
    enum { value = true };
};

template <typename T, std::size_t N>
struct is_array_or_vector<std::array<T, N>> {
    enum { value = true };
};

template <typename T>
typename std::enable_if<is_array_or_vector<T>::value>::type
f(T const&)
{
}

int main()
{
    f(std::vector<int>());    // OK
    f(std::array<int, 17>()); // OK
    f(std::deque<int>());     // ERROR
}
like image 78
Dietmar Kühl Avatar answered Oct 12 '22 19:10

Dietmar Kühl


Why not just use this, which works with any container using random-access iterators, including plain old arrays. If you can use iteration instead of indexing, you can do away with the random-access requirement as well.

template <typename Cnt1, typename Cnt2>
void addData(const Cnt1& yData, Cnt2 xData) // is pass-by-value intended?
{
    using std::begin;
    using std::end;
    typedef decltype(*begin(yData)) T;
    const auto sizeY = end(yData) - begin(yData);
    const auto sizeX = end(xData) - begin(xData);
    // ...
}

C++03 version (doesn't support plain old arrays):

template <typename Cnt1, typename Cnt2>
void addData(const Cnt1& yData, Cnt2 xData) // is pass-by-value intended?
{
    typedef Cnt1::value_type T;
    const size_t sizeY = yData.end() - yData.begin();
    const size_t sizeX = xData.end() - xData.begin();
    // ...
}
like image 45
Ben Voigt Avatar answered Oct 12 '22 19:10

Ben Voigt


An alternative solution:

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

using std::vector;
using std::array;

template <typename Container>
struct container_helper; // undefined

template <typename T>
struct container_helper<vector<T>>
{    
    explicit container_helper(vector<T>& data)
    : _data(data)
    {}

    T* get_data()
    { return &_data[0]; }

    size_t get_size()
    { return _data.size(); }

private:
    vector<T>& _data;
};

template <typename T, size_t N>
struct container_helper<array<T,N>>
{
    explicit container_helper(array<T,N>& data)
    : _data(data)
    {}

    T* get_data()
    { return &_data[0]; }

    size_t get_size()
    { return N; }

private:
    array<T,N>& _data;
};


template <typename Container1, typename Container2>
void add_data(Container1& c1, Container2& c2)
{
    container_helper<Container1> c1_helper(c1);
    container_helper<Container2> c2_helper(c2);

    /* do whatever you want with the containers */
    std::cout << "c1 size " << c1_helper.get_size() << std::endl;
    std::cout << "c2 size " << c2_helper.get_size() << std::endl;
}


int main()
{
    vector<int > v_ints(3);
    array<int, 2> a_ints;

    add_data(v_ints, a_ints);
}
like image 36
Gigi Avatar answered Oct 12 '22 19:10

Gigi