views:

168

answers:

3

I am wondering if there is any benefit to getting a reference to a vector prior to calling BOOST_FOREACH or whether a method call which returns a reference will be automatically used? For example which of the two following loops will be equivalent to the third loop?

vector<float>& my_method();

void main()
{
    // LOOP 1 -------------------------------
    vector<float>& temp_vector = my_method();
    BOOST_FOREACH(float element, temp_vector)
        cout << element << endl;

    // LOOP 2 -------------------------------
    vector<float> temp_vector = my_method();
    BOOST_FOREACH(float element, temp_vector)
        cout << element << endl;

    // Which loop is this one most like? ----
    BOOST_FOREACH(float element, my_method())
        cout << element << endl;
}
A: 

And although BOOST_FOREACH is a macro, it is a remarkably well-behaved one. It evaluates its arguments exactly once, leading to no nasty surprises

so my_method() won't be called more than once

References behaves like "normal" variables from the user point of view, you can treat the reference exactly as if it were the original variable. It makes no difference to the foreach loop.

So both loops are equivalent.

f4
doesn't address the reference part of the question
Jamie Cook
I updated the answer
f4
still not really addressing the core question, the result of the method call must be stored in a temporary variable... when leaving it up to the macro to decide this, will it be stored in a reference? or will the result be copied into a new vector (incuring a performance penalty in the processs) - see updated question.
Jamie Cook
+2  A: 

A quick test shows that the function is called once and no copying occurs in connection with BOOST_FOREACH.

#include <vector>
#include <iostream>
#include <boost/foreach.hpp>

struct X
{
    X() {}
    X(const X& ) { std::cout << "copied\n"; }
};

std::vector<X> vec(2);

//std::vector<X> method()
std::vector<X>& method()
{
    std::cout << "returning from method\n";
    return vec;
}

int main()
{
    BOOST_FOREACH(const X& x, method()) {}
}
visitor
Thanks for the excellent example code, if @Checkers hadn't posted excerpts from the actual boost metaprogramming I would have given you the answer :)
Jamie Cook
A: 

Looking through BOOST_FOREACH metaprogramming madness I see that the collection gets copied if it's

  1. an rvalue,
  2. a "lightweight proxy", which you can define for your types by specializing boost::foreach::is_lightweight_proxy.

Hence, lvalue is not copied. Instead, its pointer is taken as a temporary.

Crucial bit is this:

# define BOOST_FOREACH_SHOULD_COPY(COL)             \
     (true ? 0 : boost::foreach_detail_::or_(       \
         BOOST_FOREACH_IS_RVALUE(COL)               \
       , BOOST_FOREACH_IS_LIGHTWEIGHT_PROXY(COL)))

Then it's used as one of the arguments to the function that is used to evaluate a container into a temporary variable:

template<typename T> 
inline auto_any<T> contain(T const &t, boost::mpl::true_ *) // rvalue 
{ 
    return t;
}

template<typename T>
inline auto_any<T *> contain(T &t, boost::mpl::false_ *) // lvalue
{
    // Cannot seem to get sunpro to handle addressof() with array types.
    #if BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x570))
    return &t;
    #else
    return boost::addressof(t);
    #endif
}

I have Boost v1.38 installed in my system.

Alex B