Andy
Andy

Reputation: 1231

Why is a vector treated as if it was passed by value even when it is passed by const reference?

I was recently coding a very simple class, which is responsible for finding the min and max values in a std::vector and found out, that even when I pass the collection as a const reference to the class' constructor and then change the vector (ie. push an element into it or remove one from it) from outside, the vector inside the class remains unchanged.

#include <vector>
#include <algorithm>

class MinMaxFinder
{
public:
    MinMaxFinder(const std::vector<int>& numbers) : numbers(numbers)
    {
    }
    const int Min() const
    {
        this->findMinAndMax();
        return this->min;
    }
    const int Max() const
    {
        this->findMinAndMax();
        return this->max;
    }

protected:
    std::vector<int> numbers;
    int min;
    int max;

    void findMinAndMax()
    {
        // std::minmax_element call to find min and max values
    }
};

I assumed the point of passing a reference is so that a large object is not copied, the way my code works now, however, seems like it does copy the collection.

#include <vector>
#include <iostream>
#include "MinMaxFinder.h"

int main(int argc, char* argv[])
{
    std::vector<int> v{ 1, -6, 12, 158, -326 };
    MinMaxFinder mmf(v);

    v.push_back(-9999999);
    v.push_back(9999999);

    auto min = mmf.Min();
    auto max = mmf.Max();
}

The value of min will be -326, the value of max will be 158, even though it is pretty clear that the value 9999999 is much larger than 158.

I managed to fix this behaviour by changing the definition of the private member numbers to std::vector<int>& numbers, but is it the correct solution?

Still, why would the state of internal collection remain unchaged, does it not defy the definition of passing by const ref?

Upvotes: 1

Views: 145

Answers (4)

Bathsheba
Bathsheba

Reputation: 234785

The member initialisation numbers(numbers) called on construction will take a deep copy.

If you were to compute the minimum and maximum values on construction, then you wouldn't need to retain numbers as a class member.

You could store a reference as the class member, but then you'd have to be careful to limit the lifetime of your class instance to that where the vector is in scope, else the reference could dangle. I'd advise against doing that.

Upvotes: 3

David Haim
David Haim

Reputation: 26506

std::vector<int> numbers; should be const std::vector<int>& numbers; this way you reference an object without copy it or having the ability of changing it.

but seriously, what is the point of this class? huge overkill! just use std::min_element and std::max_element or std::minmax_element..

auto minimun = std::min_element(numbers.begin(),numbers.end());

Upvotes: 6

songyuanyao
songyuanyao

Reputation: 172964

The member varialbe numbers is not declared as reference, so it will be copied when get initialized, even though the ctor's parameter is passed by reference. Try to change the declaration to:

protected:
    const std::vector<int>& numbers;

Upvotes: 0

Lightness Races in Orbit
Lightness Races in Orbit

Reputation: 385274

I assumed the point of passing a reference is so that a large object is not copied

Yeah, that's right, and the large object isn't copied when "calling" the constructor.

However, your data member is a value, not a reference, so the argument is immediately copied to initialise the member.

It's like writing this:

std::vector<int> v;
const std::vector<int>& ref = v;
std::vector<int> v2 = ref;

then wondering why v2 is a copy. :)

Upvotes: 3

Related Questions