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

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


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
$

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