c++ - arguments to tuple are copied instead of moved when returning the tuple from a function -


i have question following code. compiler msvc++ 17 visual studio version 15.3 compiler option /std:c++14 (as opposed /std:c++latest) running in release mode:

struct bar {     int a;     std::string b;     bar() { std::cout << "default\n";  }     bar(int a, const std::string& b)    : a{ }, b{ b } { std::cout << "direct\n"; }     bar(int a, std::string&& b)         : a{ }, b{ std::move(b) } { std::cout << "direct move b\n"; }     bar(const bar& other)               : a{ other.a }, b{ other.b } { std::cout << "const copy\n"; }     bar(bar&& other)                    : a{ std::move(other.a) }, b{ std::move(other.b) } { std::cout << "move\n"; }     bar& operator=(const bar& other)     {         = other.a;         b = other.b;         std::cout << "const assign\n";         return *this;     }      bar& operator=(bar&& other)     {         = std::move(other.a); //would correct?         b = std::move(other.b);          std::cout << "move assign\n";         return *this;     } };  std::tuple<bar, bar> foo() {     std::string s = "dsdf";     return { { 1, s }, { 5, "asdf" } }; }  int main() {     bar a, b;     std::tie(a, b) = foo();     std::cout << a.a << a.b << std::endl;     std::cout << b.a << b.b; } 

the output is:

default default direct direct move b const copy <-- why copy? why not move> const copy <-- why copy? why not move> move assign move assign 1dsdf 5asdf 

if change return { { 1, s }, { 5, "asdf" } }; return { bar{ 1, s }, bar{ 5, "asdf" } }; output changes to:

default default direct direct move b move move move assign move assign 1dsdf 5asdf 

question: why isn't move performed in both cases? why copy constructor called in first case?

the simplest distillation of question why:

std::tuple<bar> t{{5, "asdf"}}; 

prints

direct move b const copy 

but

std::tuple<bar> u{bar{5, "asdf"}}; 

prints

direct move b move 

to answer question, have determine 2 declarations do. , in order that, have understand of std::tuple's constructors called. relevant ones (neither explicitness , constexprness of each constructor relevant, omitting them brevity):

tuple( const types&... args ); // (2)  template< class... utypes > tuple( utypes&&... args );     // (3) 

initializing bar{5, "asdf"} invoke constructor (3) better match (both (2) , (3) viable, less cv-qualified reference in (3)), forward utypes tuple. why end move.

but initializing {5, "asdf"}, constructor isn't viable because braced-init-lists have no type can deduced. hence our only option (2), , end copy.

the way fix add non-template constructors take rvalue references each of types. need 2^n-1 such constructors (all 1 takes const lvalue references - since 1 deduced), instead end design works in cases suboptimal. since specify type want on call site, isn't huge defect.


Comments

Popular posts from this blog

PHP and MySQL WP -

android - InAppBilling registering BroadcastReceiver in AndroidManifest -

go - golang pprof for c library code -