Question

I had this problem while using a given library in my software. A function returns a const std::vector& which I want to use. However, the first value is always set to zero (or in my original code: a 3D-vector with all coordinates set to 0.0) although the value I stored in it beforehand was not.

The class structure is the following (the example code is below):

  • ValueContainer: contains a std::vector< double > of values and the getter values().
  • Object: contains a ValueContainer, and a getter valuesContainer().

In my code, I retrieved the values with:

// First element always set to 0
const std::vector< double >& values = object.valuesContainer().values();

This resulted in the error, that the first element was set to 0.0. I could circumvent the problem by copying the values:

// This works as expected
std::vector< double > values = object.valuesContainer().values();

On closer inspection, I noticed, that valuesContainer() did not return a reference, but a copy of ValueContainer. What happens is (in my understanding), that I call values() on a temporary object created by valuesContainer() and thus get a reference to a temporary object. This of course will not work well, and I would expect to get 'garbage'-data in my vector, or something like that.

However: All the values except the first one seem to be ok! Now, the real question is: Why does it work like that? If it is just undefined behaviour, why is the first value set to 0 and the others remain the same? I'd be happy to get a little more insight into that matter.

Here now follows the example code, tested with gcc 4.8.1 on linux:

#include <iostream>
#include <iomanip>
#include <vector>

class ValueContainer
{
    public:
        ValueContainer();
        inline const std::vector< double >& values() const;

    //private:
        std::vector< double > some_values_;
};

class Object
{
    public:
        ValueContainer valueContainerCopy() const
        {
            return values_;
        }

        const ValueContainer& valueContainerConstRef() const
        {
            return values_;
        }

    //private:
        ValueContainer values_;
};

ValueContainer::ValueContainer()
{
    // Just some test data
    some_values_.push_back(1.2);
    some_values_.push_back(3.4);
    some_values_.push_back(5.6);
    some_values_.push_back(7.8);
    some_values_.push_back(9.0);
}

const std::vector< double >& ValueContainer::values() const
{
    return some_values_;
}


int main( int argc, char** argv )
{
    Object obj;

    const std::vector< double >& values_CopyRef  = obj.valueContainerCopy().values();
    const std::vector< double >& values_RefRef   = obj.valueContainerConstRef().values();
    std::vector< double >        values_CopyCopy = obj.valueContainerCopy().values();

    std::cout << "Pointers: " << std::endl
              << " - Original: " << &obj.values_.some_values_ << std::endl
              << " - CopyRef:  " << &values_CopyRef << std::endl
              << " - RefRef:   " << &values_RefRef << std::endl
              << " - CopyCopy: " << &values_CopyCopy << std::endl;

    std::cout << "Data pointers: " << std::endl
              << " - Original: " << obj.values_.some_values_.data() << std::endl
              << " - CopyRef:  " << values_CopyRef.data() << std::endl
              << " - RefRef:   " << values_RefRef.data() << std::endl
              << " - CopyCopy: " << values_CopyCopy.data() << std::endl;

    std::cout << "Data:" << std::endl;
    for ( std::size_t i = 0; i < values_RefRef.size(); i++ )
    {
        std::cout << "i=" << i << ": " <<  std::fixed << std::setprecision(1)
                  << "CopyRef: "  << values_CopyRef[ i ] << ", "
                  << "RefRef: "   << values_RefRef[ i ] << ", " 
                  << "CopyCopy: " << values_CopyCopy[ i ] 
                  << std::endl;
    }
    return 0;
}

The (important) output of this example is the following, where you can see, that the first value of the reference to the temporary object is set to 0 and the other values seem fine:

i=0: CopyRef: 0.0, RefRef: 1.2, CopyCopy: 1.2
i=1: CopyRef: 3.4, RefRef: 3.4, CopyCopy: 3.4
i=2: CopyRef: 5.6, RefRef: 5.6, CopyCopy: 5.6
i=3: CopyRef: 7.8, RefRef: 7.8, CopyCopy: 7.8
i=4: CopyRef: 9.0, RefRef: 9.0, CopyCopy: 9.0
Était-ce utile?

La solution

const std::vector< double >& values_CopyRef  = obj.valueContainerCopy().values();

That is storing a reference to a temporary because the valueContainerCopy function is declared as

ValueContainer valueContainerCopy() const
{
    return values_;
}

Which will copy the vector values_ into a temporary vector that is returned. You are then storing a reference to data in that temporary, resulting in undefined behavior.

If you want to store it as a reference, you need to return a reference, which is what you do in the valueContainerConstRef function.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top