Reputation:
I'm trying to achieve the following optimization in my container library:
The optimization is supposed to be useful e.g. if contained element type is something like std::vector
, where moving if possible would give substantial speedup.
However, so far I was unable to devise any working scheme for this. My container is quite complicated, so I can't just duplicate insert()
code several times: it is large. I want to keep all "real" code in some inner helper, say do_insert()
(may be templated) and various insert()
-like functions would just call that with different arguments.
My best bet code for this (a prototype, of course, without doing anything real):
#include <iostream>
#include <utility>
struct element
{
element () { };
element (element&&) { std::cerr << "moving\n"; }
};
struct container
{
void insert (const element& value)
{ do_insert (value); }
void insert (element&& value)
{ do_insert (std::move (value)); }
private:
template <typename Arg>
void do_insert (Arg arg)
{ element x (arg); }
};
int
main ()
{
{
// Shouldn't move.
container c;
element x;
c.insert (x);
}
{
// Should move.
container c;
c.insert (element ());
}
}
However, this doesn't work at least with GCC 4.4 and 4.5: it never prints "moving" on stderr. Or is what I want impossible to achieve and that's why emplace()
-like functions exist in the first place?
Upvotes: 3
Views: 272
Reputation: 41351
I think you may need to forward the argument:
template <typename Arg>
void do_insert (Arg&& arg)
{ element x (std::forward<Arg>(arg)); }
Full code:
#include <iostream>
#include <utility>
struct element
{
element () { };
element (const element&) { std::cerr << "copying\n"; }
element (element&&) { std::cerr << "moving\n"; }
};
struct container
{
void insert (const element& value)
{ do_insert (value); }
void insert (element&& value)
{ do_insert (std::move(value)); }
private:
template <typename Arg>
void do_insert (Arg&& arg)
{ element x (std::forward<Arg>(arg)); }
};
int
main ()
{
{
// Shouldn't move.
container c;
element x;
c.insert (x);
}
{
// Should move.
container c;
c.insert (element ());
}
}
The keyword that you might look for is "perfect forwarding".
Upvotes: 2
Reputation:
I can't say I understand why this works and some other code doesn't, but this seems to do the trick (created thanks to hints from Potatoswatter):
#include <iostream>
#include <utility>
struct element
{
element () { };
element (const element&) { std::cerr << "copying\n"; }
element (element&&) { std::cerr << "moving\n"; }
};
struct container
{
void insert (const element& value)
{ do_insert <const element&> (value); }
void insert (element&& value)
{ do_insert <element&&> (std::forward <element&&> (value)); }
private:
template <typename Arg>
void do_insert (Arg arg)
{ element x (std::forward <Arg> (arg)); }
};
int
main ()
{
std::cerr << "1\n";
{
// Shouldn't move.
container c;
element x;
c.insert (x);
}
std::cerr << "2\n";
{
// Should move.
container c;
c.insert (element ());
}
}
I get the following output with both GCC 4.4 and 4.5:
1
copying
2
moving
Upvotes: 0
Reputation: 137870
I'd recommend to copy the way it's done in your STL implementation (or GNU, which should be able to read online anyway).
But
template <typename Arg>
void do_insert (Arg arg)
{ element x (move(arg)); }
might do the trick.
This functionality is separate from emplace
and you're correct that it works in the standard library.
EDIT I made some changes and discovered
move
but the original object was not moved. So focus on finding a temporary which is being moved… this isn't actually such a bad thing..
#include <iostream>
#include <utility>
struct element
{
element () : moved(false) { };
element (element&&) { moved = true; std::cerr << "moving\n"; }
bool moved;
};
struct container
{
void insert (const element& value)
{ do_insert (value); }
void insert (element&& value)
{ do_insert (std::move (value)); }
private:
template <typename Arg>
void do_insert (Arg arg)
{ element x (std::move(arg)); }
};
int
main ()
{
std::cerr << "try 1\n";
{
// Shouldn't move.
container c;
element x;
c.insert (x);
std::cerr << x.moved << "\n";
}
std::cerr << "try 2\n";
{
// Should move.
container c;
c.insert (element ());
}
}
Upvotes: 0