0

I have a large data set (several gigabytes) stored in a file that does not fit into QVector (max ~2gb) that is copy-on-write and would not suffer from this. I load the vector into memory like this:

std::vector<int> load()
{
    std::vector<int> vec;
    QFile file("filename");
    file.open(QIODevice::ReadOnly);
    QDataStream stream(&file);
    stream >> vec; //using my implementation of QDataStream &operator>>(QDataStream &stream, std::vector<int> &v)
    return vec;
}

and pass it into this:

class Data
{
public:
    Data(const std::vector<int> &data) :
        d(data)
    {
    }

private:
    std::vector<int> d;
};

like that:

Data data(load());

Now in load() the vector is created and filled with data from the file. Then it is returned by value but I believe it should be optimized by the compiler not to actually copy the vec but rather move it to the destination.

The destination is the private member of Data but I suspect that the initializer list in the constructor might actually copy the vector rather than moving it as it does not know (or does it?) it has received a const reference to a temporary.

Is there a way to guarantee that the vector will be only moved after construction and not copied in this kind of scenario? I suspect using std::move, std::forward or changing signature of the Data::Data() to take r-value reference?

Resurrection
  • 3,916
  • 2
  • 34
  • 56
  • 3
    Since you're already in the business of suspecting all those things, what's stopping you from just *trying* them? – Kerrek SB Mar 08 '17 at 07:38
  • 2
    @KerrekSB The fact that I will not be using only one compiler and I would rather **know** what will work than try it on all of them stumbling over optimizations and whatnot... – Resurrection Mar 08 '17 at 07:41
  • 2
    There is already a better answer, but note that letting a shared_ptr (or unique_ptr if you can manage) own the vector would be a rather simple way to avoid copies. – Marc Glisse Mar 08 '17 at 07:59

1 Answers1

6

You can add a constructor that takes an rvalue reference, then copy-construct the data member with it:

Data(std::vector<int>&& data) :
    d(std::move(data)) {}

Data(const std::vector<int>& data) // as before

There's a shorter version, where you replace both constructor overloads by one, taking the parameter by value. Note that this relies on copy elision of rvalue arguments to avoid any extra move copies. It can potentially do more work than the two overload solution, but the work is minimal compared to copying the contents of a large vector*. It also and may require some extra head scratching, although it has become somewhat idiomatic:

Data(std::vector<int> data) :
    d(std::move(data)) {}

* See this related post, particularly Howard Hinnant's answer.

Community
  • 1
  • 1
juanchopanza
  • 223,364
  • 34
  • 402
  • 480
  • 2
    Note that doing the first solution, then eliminating the `const&` constructor, *prevents* a user from accidentally copying the vector into `Data` -- they *must* move it in. If they want to copy, they have to create a temporary and move it into `Data` explicitly. This may be worth it. – Yakk - Adam Nevraumont Mar 08 '17 at 13:17