[Development] qMoveToConst helper for rvalue references to movable Qt containers?
Elvis Stansvik
elvstone at gmail.com
Sun Oct 28 12:13:50 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;
> }
>
> 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
> $
I tried some more compilers.
With Apple Clang 9.0.0 and -std=c++14, the output is the same without
-fno-elide-constructors, but interestingly, if -fno-elide-constructors
is added, the output is:
with returning const:
0x7fff596ed8a8 constructed
0x7fff596eda20 move constructed
0x7fff596ed8a8 destructed
0x7fff596eda20 begin const
0x7fff596eda20 end const
0x7fff596eda20 destructed
with recommended way:
0x7fff596ed8a8 constructed
0x7fff596ed9c8 move constructed
0x7fff596ed8a8 destructed
0x7fff596ed9e0 move constructed
0x7fff596ed9c8 destructed
0x7fff596ed9e0 begin const
0x7fff596ed9e0 end const
0x7fff596ed9e0 destructed
With MSVC 2015 (compiled with just cl copytest.cpp), the output is:
with returning const:
0000009FFE4BFD90 constructed
0000009FFE4BFE88 move constructed
0000009FFE4BFD90 destructed
0000009FFE4BFE88 begin const
0000009FFE4BFE88 end const
0000009FFE4BFE88 destructed
with recommended way:
0000009FFE4BFD90 constructed
0000009FFE4BFEA0 move constructed
0000009FFE4BFD90 destructed
0000009FFE4BFEA0 begin const
0000009FFE4BFEA0 end const
0000009FFE4BFEA0 destructed
So somehow it invokes the move constructor, not sure if this is just a bug.
Elvis
>
> 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