c++ - Two-step copy elision to capture rvalue in constructor call as instance variable -
i trying rvalue
instance of class:
#include <iostream> #define msg(x) std::cout << x " constructor\n" struct x { int i; x(int i) : i(i) {msg("x");} x(const x& x) : i(x.i) {std::cout << "x copy\n";} x(x&& x) {std::swap(i, x.i); std::cout << "x move\n";} };
into instance variable x
of class:
struct { x x; a(x x) : x(x) {msg("a");} };
like so:
int main() { a(x(1)); std::cout << a.x.i << "\n\n"; }
without copies or moves being made.
according these references,
- http://thbecker.net/articles/rvalue_references/section_01.html
- http://www.slideshare.net/oliora/hot-11-2-new-style-arguments-passing
and many many posts on (so please read end before flagging duplicate), should rely on copy elision, conditions should satisfied if pass value. note there two copy elisions required, namely:
constructor call -> constructor local variable -> instance variable
as can seen when turning copy elision off (compile g++-4.8 -std=c++11 -fno-elide-constructors
):
x constructor x move x copy constructor 1
so there 1 move
step , 1 copy
step, should both go away if turn copy elision on (compile g++-4.8 -std=c++11 -o3
):
x constructor x copy constructor 1
bummer, copy
step remained!
can better other variation of std::move()
, std::forward
or passing rvalue-reference
?
struct b { x x; b(x x) : x(std::move(x)) {msg("b");} }; struct c { x x; c(x x) : x(std::forward<x>(x)) {msg("c");} }; struct d { x x; d(x&& x) : x(std::move(x)) {msg("d");} }; int main() { b b(x(2)); std::cout << b.x.i << "\n\n"; c c(x(3)); std::cout << c.x.i << "\n\n"; d d(x(4)); std::cout << d.x.i << "\n\n"; }
which produces output:
x constructor x move b constructor 2 x constructor x move c constructor 3 x constructor x move d constructor 4
ok, turned copy
move
, but not satisfactory!
next, tried make instance variable x
reference x&
:
struct e { x& x; e(x x) : x(x) {msg("e");} }; int main() { e e(x(5)); std::cout << e.x.i << "\n\n"; }
which produces:
x constructor e constructor 1690870696
bad idea! got rid of move
rvalue
instance x
referencing got destroyed under seat, last line prints garbage instead of 5
. 2 notes:
g++-4.8
didn't warn me of anything,-pedantic -wall -wextra
- the program prints
5
when compiled-o0
so bug may go unnoticed quite while!
so, hopeless case? no:
struct f { x& x; f(x& x) : x(x) {msg("f");} }; int main() { x x(6); f f(x); std::cout << f.x.i << "\n"; }
prints:
x constructor f constructor 6
really? no fancy new c++11
features, no copy elision @ discretion of compiler, plain old fortran66-style pass-by-reference want , perform best?
so here questions:
- is there way 1 can work
rvalues
? did miss features? - is
lvalue
-reference version best, or there hidden costs inx x(6)
step? - could there inconveniences introduced
x
living on after construction off
? - could pay data locality penalty using
lvalue
reference external instance?
without going detail of question, copy elision used as possible. here's quick demo:
#include <iostream> #include <utility> struct x { int n_; explicit x(int n) : n_(n) { std::cout << "construct: " << n_ << "\n"; } x(x const & rhs) : n_(rhs.n_) { std::cout << "x copy:" << n_ << "\n"; } x(x && rhs) : n_(rhs.n_) { rhs.n_ = -1; std::cout << "x move:" << n_ << "\n"; } ~x() { std::cout << "destroy: " << n_ << "\n"; } }; struct { explicit a(x x) : x_(std::move(x)) {}; x x_; }; struct b { x x; }; int main() { a(x(12)); b b { x(24) }; }
this produces:
construct: 12 x move:12 destroy: -1 construct: 24 destroy: 24 destroy: 12
the 1 move in x_(std::move(x))
not elidable, since doesn't involve function return. that's pretty anyway. , notice how aggregate b
indeed initialized "in-place".
your example f
shows you're willing expose coupling of x
ambient class. in case, make special constructor a
constructs x
directly:
struct { explicit a(x x) : x_(std::move(x)) {}; x x_; // better approach struct direct_x_t {}; static direct_x_t direct_x; // in our case, suffices: a(direct_x_t, int n) : x_(n) {} // generally, template may better: (todo: add sfinae control) template <typename ...args> a(direct_x_t, args &&... args) : x_(std::forward<args>(args)...) {} };
usage:
a a(a::direct_x, 36);
Comments
Post a Comment