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
Post a Comment