5

I have a problem with this bit of code:

#include <boost/multi_array.hpp>
#include <boost/array.hpp>
#include <vector>
#include <iostream>

template <typename Vec>
void foo(Vec& x, size_t N)
{
    for (size_t i = 0; i < N; ++i) {
        x[i] = i;
    }
}

int main()
{
    std::vector<double> v1(10);
    foo(v1, 5);
    std::cout << v1[4] << std::endl;


    boost::multi_array<double, 2> m1;
    boost::array<double, 2> shape;
    shape[0] = 10;
    shape[1] = 10;
    m1.resize(shape);
    foo(m1[0], 5);
    std::cout << m1[0][4] << std::endl;
    return 0;
}

Trying to compile it with gcc, I get the error:

boost_multi_array.cpp: In function 'int main()':
boost_multi_array.cpp:26: error: invalid initialization of non-const reference of type 'boost::detail::multi_array::sub_array<double, 1u>&' from a temporary of type 'boost::detail::multi_array::sub_array<double, 1u>'
boost_multi_array.cpp:7: error: in passing argument 1 of 'void foo(Vec&, size_t) [with Vec = boost::detail::multi_array::sub_array<double, 1u>]'

It works as expected for boost::multi_array when I change the type of the first argument of function foo from Vec& to Vec, but then the std::vector is passed by value, which is not what I want. How can I achieve my goal without writing two templates?

2 Answers 2

1

The problem is that for NumDims > 1, operator[] returns a temporary object of type template subarray<NumDims-1>::type.

A (not so nice) work-around would be the something like the following:

typedef boost::multi_array<double, 2> MA;
MA m1;
MA::reference ref = m1[0];
foo(ref, 5); // ref is no temporary now

An alternative would be to wrap your implementation and provide an overload for the multi-array case.... E.g.:

(note: i didn't see how to get the overload to work with boost::multi_array<T,N>::reference, please don't put it into productive use with this detail:: version ;)

template<class T>
void foo_impl(T x, size_t N) {
    for (size_t i = 0; i < N; ++i) {
        x[i] = i;
    }
}

template<class T>
void foo(T& t, size_t n) {
    foo_impl<T&>(t, n);
}

template<typename T, size_t size>
void foo(boost::detail::multi_array::sub_array<T, size> r, size_t n) {
    foo_impl(r, n);
}
Sign up to request clarification or add additional context in comments.

3 Comments

Could this be solved elegantly using boost::enable_if_c and boost::traits?
The MA::reference ref = m1[0] line definitely isn't as concise syntactically, but it doesn't look to add significant overhead at runtime for the std::vector case. I recently ran afoul of the same problem: old.nabble.com/…
@quant_dev: that also has the problem of why using boost::multi_array<T,N>::reference directly doesn't work, i don't have time to investigate that further.
0

The solution (for a modern era) should be easy, take by universal reference:

template <typename Vec>
void foo(Vec&& x, size_t N)
{
    for (size_t i = 0; i < N; ++i) {
        x[i] = i;
    }
}

and since you are at it, make the implementation more correct (forward), return something useful, and name it well:

template <typename Vec>
void iota_first_n(Vec&& x, size_t N)
{
   auto const last = x.begin() + N;
   std::iota(std::forward<Vec>(x).begin(), last, std::size_t{0});
   return last;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.