views:

351

answers:

1

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?

+1  A: 

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);
}
Georg Fritzsche
Could this be solved elegantly using boost::enable_if_c and boost::traits?
quant_dev
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: http://old.nabble.com/-multiarray--problems-passing-views-by-reference-td27039405.html
Rhys Ulerich
@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.
Georg Fritzsche
No problem. Thanks for the first workaround.
quant_dev