vendredi 27 mars 2015

How to allocate memory and use std::tie at the same time (returning a tuple without reallocating memory)

Is there a good way to use std::tie and allocate memory at the same time? In other words, if a function returns a std:tuple and we want to ultimately break up the result into individual components, is there a way to do these assignments without preallocating memory?


For example, consider the following code:



#include <tuple>
#include <limits>

struct Foo {
int val;
Foo(int const & val_) : val(val_) {}
Foo() = delete;
~Foo() = default;
Foo(Foo && foo) {
val = foo.val;
foo.val = std::numeric_limits <int>::min();
}
Foo & operator = (Foo && foo) {
val = foo.val;
foo.val = std::numeric_limits <int>::min();
return *this;
}
Foo(Foo const &) = default;
Foo & operator = (Foo const &) = default;
};

struct Bar {
Foo foo;
int lav;
Bar(int const & val_,int const & lav_) : foo(val_), lav(lav_) {}
Bar() = delete;
~Bar() = default;
Bar(Bar &&) = default;
Bar & operator = (Bar &&) = default;
Bar(Bar const &) = default;
Bar & operator = (Bar const &) = default;
};

std::tuple <Foo,Bar&&> f(Bar && bar) {
Foo foo = std::move(bar.foo);
return std::tuple<Foo,Bar&&> (foo,std::move(bar));
}

int main() {
auto bar = Bar(1,2);
#if 1
auto foo_bar = f(std::move(bar));
auto foo = std::move(std::get<0>(foo_bar));
bar = std::move(std::get<1>(foo_bar));
#else
Foo foo; // Doesn't work because foo has no default constructor
std::tie(foo,bar) = f(std::move(bar));
#endif

int junk=1;
}


Basically, the function f extracts the foo element from the object bar and returns it separately. Using gdb, we see this works as expected:



42 auto foo_bar = f(std::move(bar));
(gdb) n
43 auto foo = std::move(std::get<0>(foo_bar));
(gdb)
44 bar = std::move(std::get<1>(foo_bar));
(gdb)
50 int junk=1;
(gdb) print foo
$1 = {val = 1}
(gdb) print bar
$2 = {foo = {val = -2147483648}, lav = 2}


Nevertheless, here, we allocated into the tuple foo_bar first and then extracted the elements one at a time. Instead, we could try to use std::tie by using the other side of the ifdef. However, it doesn't work because we don't have a default constructor for foo.


Now, in this case, we could just allocate a foo by giving it a default constructor or by giving it a dummy value for its existing constructor. However, in general, this has been an issue for me since I have more complicated classes that don't have easy constructors. Really, I'd like to just have a statement like std::tie that pushes a tuple into individual elements and allocates memory at the same time. Is this possible?


Aucun commentaire:

Enregistrer un commentaire