Suppose I have an array B
that has been already defined and used somewhere in a C++ code. Now, suppose that I have another array A
that has been de
The easiest way it to use std::vector
or std::array
example:
vector<int> f(const vector<int>& a) {
return a;
}
b = f(a);
Actually, you don't have to use one of this classes to store, you may use your own class, that have operator =
YourClass& operator = (const vector<int>&/* there will be returned value of f, it may be std::array, std::vector or your own class.*/ v) {
//fill it here.
return *this;
}
You may also provide move constructor for it to avoid unnecessary copying.
for example
class Array {
int* data;
YourClass& operator = (YourClass&& v) {
swap(v.data, data);
}
}
YourClass f(const YourClass& a) {
//
}
Array a,b;
b = f(a);
Both are possible (or very nearly, f(a)
), if you are willing to use a custom type for b
. The idea is that you already have b
, hence you want to semantically pass it as an argument to f
. For example, we can overload the assignment operator for that:
First, define an expression concept (here represented as an abstract class, but we don't really want to use virtual dispatch). This encapsulates a computation, that can Evaluate
and returns the result in the target
.
template<typename T>
class Expression
{
public:
virtual void Evaluate(T& target) const = 0;
}
Now define your array class, that can be assigned from an Expression
class. When an expression is assigned, it is evaluated.
template<typename T>
class Array
{
T* ptr;
public:
template<typename Expression>
const Array<T> operator =(Expression const & expression)
{
expression.Evaluate(ptr);
}
}
Now, define your function f
to return an expression, that when evaluated, returns f(a)
.
class MyExpression
{
float* a;
public:
MyExpression(float* a) : a(a) {}
void Evaluate(float* target)
{
f(a, b);
}
}
MyExpression<float> f(float* a)
{
return MyExpression(a);
}
Which you can then call as follows
Array b;
b = f(a);
This does create a temporary (on the stack), but it could be inlined away. For example, suppose we have the call
b = f(a);
this is equivalent to
b.operator = (f (a));
inlining the call to operator =
, we get
(f(a)).Evaluate(b);
now, we can also inline the call to f
, giving
(MyExpression(f)).Evaluate(b);
now, the compiler can just notice that this is exactly the same as doing
f(a, b);
and do that straight away. Hence, no need to create the temporary MyExpression
, though its cost is negligible in the first place (it's a stack allocation of sizeof(Array)
)
Note that this type of strategy is used by the matrix library eigen to avoid temporaries for example.
Since f
doesn't have any references to B
, it has no way but work on a local array. You would therefore then have to copy the values to B
. So in the current form, no there is no way. However, if you make f
inline, the optimizer may just help you with that1, but that wouldn't be a good idea for an FFT for example.
With temporary values but no memory leak, you can simply wrap the array in a class (or use vector
which already does that) and return that. Note that non-dynamic-array copy itself doesn't produce memory leaks, it's just not possible in C++ to write some_array = another_array
.
If you have the option for a redesign, best way would be to call f(A, B)
for maximum performance,
1 If the compiler is smart enough, it would recognize that a local array in f
is going to be copied over to B
, and in the inline version of f
it could use B
itself since it has access to it.
First, it depends on what you mean by array. With the usual C++
meaning (std::vector
), there will never be any problem with
memory leaks; with the usual C meaning (T[]
), B = f( A )
is
an illegal. If you define your own array type, then it should
behave more or less like std::vector
in this respect.
With regards to extra temporaries: f
should take its argument
as a const reference, so that there will be no copying to pass
the argument. As for the return value, there is formally a
copy, but the compiler is allowed to alide it, and most do, at
least some of the time. In an assignment statement, the actual
data of the array will probably be copied in the assignment
itself.
In C++11, you can provide a move constructor and a move assignment operator, and (probably) reduce the chances of a copy even more.