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.8didn't warn me of anything,-pedantic -wall -wextra- the program prints
5when 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
xliving on after construction off? - could pay data locality penalty using
lvaluereference 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