[Development] qMoveToConst helper for rvalue references to movable Qt containers?

Elvis Stansvik elvstone at gmail.com
Sun Oct 28 11:28:17 CET 2018


Den sön 28 okt. 2018 kl 11:22 skrev Elvis Stansvik <elvstone at gmail.com>:
>
> Den lör 27 okt. 2018 kl 22:23 skrev Thiago Macieira <thiago.macieira at intel.com>:
> >
> > On Saturday, 27 October 2018 08:33:30 PDT Sérgio Martins wrote:
> > > Should we instead just encourage people to make returnsQtContainer()
> > > return a const container ?
> >
> > We should not, since the prevents move-construction from happening. You'll pay
> > the cost of two reference counts (one up and one down).
>
> Though hmm, even if we'd lose move-construction, for the copy we'd get
> instead, wouldn't copy elision kick in and elide it? So we wouldn't
> have to pay for the ref count up/down?
>
> I simplified my example a little, so to recap:
>
> $ cat copytest.cpp
> #include <iostream>
> #include <vector>
>
> struct Foo {
>     Foo() {
>         std::cout << this << " constructed" << std::endl;
>     }
>     Foo(const Foo &) {
>         std::cout << this << " copy constructed" << std::endl;
>     }
>     Foo(Foo &&) {
>         std::cout << this << " move constructed" << std::endl;
>     }
>     ~Foo() {
>         std::cout << this << " destructed" << std::endl;
>     }
>     std::vector<int>::iterator begin() {
>         std::cout << this << " begin" << std::endl; return v.begin();
>     };
>     std::vector<int>::const_iterator begin() const {
>         std::cout << this << " begin const" << std::endl; return v.begin();
>     };
>     std::vector<int>::iterator end() {
>         std::cout << this << " end" << std::endl; return v.end();
>     };
>     std::vector<int>::const_iterator end() const {
>         std::cout << this << " end const" << std::endl; return v.end();
>     };
>     std::vector<int> v{1, 2, 3};
> };
>
> Foo f() {
>     Foo foo;
>     return foo;
> }
>
> const Foo constF() {
>     Foo foo;
>     return foo;
> }
>
> template <typename T>
> const T moveToConst(T &&t)
> {
>     return std::move(t);
> }
>
> int main(void) {
>     std::cout << "without moveToConst:" << std::endl;
>     for (auto v : f()) { }
>
>     std::cout << std::endl;
>
>     std::cout << "with moveToConst:" << std::endl;
>     for (auto v : moveToConst(f())) { }
>
>     std::cout << std::endl;
>
>     std::cout << "with returning const:" << std::endl;
>     for (auto v : constF()) { }
>
>     std::cout << std::endl;
>
>     std::cout << "with recommended way:" << std::endl;
>     const auto stuff = f();
>     for (auto v : stuff) { }
>
>     return 0;
> }

I put it up on godbolt for this who want to play: https://godbolt.org/z/x6FPq_

Elvis

>
> Result:
>
> $ g++ -std=c++17 -O0 -o copytest copytest.cpp
> $ ./copytest
> without moveToConst:
> 0x7ffcc6ac76b0 constructed
> 0x7ffcc6ac76b0 begin
> 0x7ffcc6ac76b0 end
> 0x7ffcc6ac76b0 destructed
>
> with moveToConst:
> 0x7ffcc6ac7710 constructed
> 0x7ffcc6ac76d0 move constructed
> 0x7ffcc6ac7710 destructed
> 0x7ffcc6ac76d0 begin const
> 0x7ffcc6ac76d0 end const
> 0x7ffcc6ac76d0 destructed
>
> with returning const:
> 0x7ffcc6ac76f0 constructed
> 0x7ffcc6ac76f0 begin const
> 0x7ffcc6ac76f0 end const
> 0x7ffcc6ac76f0 destructed
>
> with recommended way:
> 0x7ffcc6ac7710 constructed
> 0x7ffcc6ac7710 begin const
> 0x7ffcc6ac7710 end const
> 0x7ffcc6ac7710 destructed
>
> So it looks like the copy has been elided also in the "with returning
> const" case. Anyone know if this is guaranteed in C++17, or just
> permitted?
>
> If I compile with -fno-elide-constructors to disable elision:
>
> $ g++ -std=c++17 -fno-elide-constructors -O0 -o copytest copytest.cpp
> $ ./copytest
> without moveToConst:
> 0x7ffe7c107070 constructed
> 0x7ffe7c1070f0 move constructed
> 0x7ffe7c107070 destructed
> 0x7ffe7c1070f0 begin
> 0x7ffe7c1070f0 end
> 0x7ffe7c1070f0 destructed
>
> with moveToConst:
> 0x7ffe7c107070 constructed
> 0x7ffe7c107150 move constructed
> 0x7ffe7c107070 destructed
> 0x7ffe7c107110 move constructed
> 0x7ffe7c107150 destructed
> 0x7ffe7c107110 begin const
> 0x7ffe7c107110 end const
> 0x7ffe7c107110 destructed
>
> with returning const:
> 0x7ffe7c107070 constructed
> 0x7ffe7c107130 move constructed
> 0x7ffe7c107070 destructed
> 0x7ffe7c107130 begin const
> 0x7ffe7c107130 end const
> 0x7ffe7c107130 destructed
>
> with recommended way:
> 0x7ffe7c107070 constructed
> 0x7ffe7c107150 move constructed
> 0x7ffe7c107070 destructed
> 0x7ffe7c107150 begin const
> 0x7ffe7c107150 end const
> 0x7ffe7c107150 destructed
> $
>
> What I find surprising here is that with the -fno-elide-constructors
> flag, in the "with returning const", the move constructor *is* called.
>
> Didn't we just say it wouldn't, because the const rvalue wouldn't bind
> to the non-const rvalue reference?
>
> I'm a little confused :p
>
> Elvis
>
> >
> > --
> > Thiago Macieira - thiago.macieira (AT) intel.com
> >   Software Architect - Intel Open Source Technology Center
> >
> >
> >
> > _______________________________________________
> > Development mailing list
> > Development at qt-project.org
> > http://lists.qt-project.org/mailman/listinfo/development



More information about the Development mailing list