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,

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 in x x(6) step?
  • could there inconveniences introduced x living on after construction of f?
  • 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

Popular posts from this blog

php - render data via PDO::FETCH_FUNC vs loop -

c++ - OpenCV Error: Assertion failed <scn == 3 ::scn == 4> in unknown function, -

The canvas has been tainted by cross-origin data in chrome only -