Reputation: 11
let me preface by saying that i'm not a c++ expert here, so i apologize in advance if what i'm trying to figure out is trivial for most. i have tried to find the solution to this in other posts, but this time i haven't succeeded.
at the end of the post you can find the standalone code that reproduces the 2 issues i'm about to detail [*].
i'm using a class A
containing a std::vector<float>
as member, which can be accessed via a const method.
loop #1
for(const auto& af : a.vec()){
std::cout << af << " (address=" << &af << ")\n";
}
loop #2
for(unsigned int i=0; i<a.vec().size(); ++i){
const auto& af = a.vec().at(i);
std::cout << af << " (address=" << &af << ")\n";
}
the 2 approaches seem to yield 2 different results. loop #1 works as expected. when using loop #2, i get that the value of some of the vector elements is now (printed out as) zero, but the memory addresses are still the right ones. naively, i expected the two approaches to give the same output. what is the reason why they differ?
vec
and a value a
chosen by the user, i'm trying to get a pointer to the element of vec
whose value is closer to a
. in the code example below, there is a routine that tries to accomplish that. the latter routine actually returns the correct pointer (which i save as a const float*
object), but then, when dereferencing, the value printed out is zero (similarly to what happens in loop#2 of issue#1). i don't understand why this happens because the original vector still exists, the memory-address is correct and non-null, but when dereferencing the value is zero, which is different from the value of the vector element pointed to.below i wrote down some instructions to reproduce these two issues.
any insight on the reason why these 2 issues arise (and/or general suggestions on the implementation below) is welcome.
thanks
[*] i'm compiling the code below (test.cc
) with g++ version 4.9.1 in SLC6, doing g++ test.cc -o test -std=c++0x
.
#include <iostream>
#include <vector>
#include <cmath>
#include <limits>
class A {
public:
explicit A() {}
virtual ~A() {}
std::vector<float> a_vec() const { return a_vec_; }
void add_obj(const float f){
a_vec_.push_back(f);
return;
}
private:
std::vector<float> a_vec_;
};
template<typename T>
const T * closest0(const float& f, const std::vector<T>& fvec){
const T* next=0;
float deltarmin = std::numeric_limits<float>::infinity();
for(unsigned int i=0; i<fvec.size(); ++i) {
const T & fi = fvec[i];
const float dr = fabs(fi-f);
if(dr < deltarmin){
deltarmin = dr;
next = &fi;
}
}
return next;
}
int main(int argc, char** argv){
if(argc-1 != 1) return 0;
const float ref(atoi(argv[1]));
////
A a;
a.add_obj(25.);
a.add_obj(35.);
a.add_obj(45.);
a.add_obj(55.);
std::cout << "\n*** input vector ***********\n";
for(unsigned int i=0; i<a.a_vec().size(); ++i){
std::cout << "a.a_vec().at(" << i << ")=" << a.a_vec().at(i) << " (address=" << &a.a_vec().at(i) << ")\n";
}
std::cout << "****************************\n";
int tryN(0);
////
++tryN;
std::cout << "\n--- loop:(const auto&)" << std::endl;
for(const auto& af : a.a_vec()){
std::cout << "#" << tryN << " " << af << " (address=" << &af << ")\n";
}
std::cout << "---------------" << std::endl;
////
++tryN;
std::cout << "\n--- loop:(index) + 'const auto& ... at()'\n";
for(unsigned int i=0; i<a.a_vec().size(); ++i){
const auto& af = a.a_vec().at(i);
std::cout << "#" << tryN << " " << af << " (address=" << &af << ")\n";
}
std::cout << "---------------" << std::endl;
////
std::cout << "\n---------------\n\n";
const float* fclo0 = closest0(ref, a.a_vec());
std::cout << " *** f-closest0(" << ref << ")=" << *fclo0 << " (address=" << fclo0 << ")\n";
return 0;
}
an example of the output i'm getting is
$ ./test 33
*** input vector ***********
a.a_vec().at(0)=25 (address=0x1a4f030)
a.a_vec().at(1)=35 (address=0x1a4f034)
a.a_vec().at(2)=45 (address=0x1a4f038)
a.a_vec().at(3)=55 (address=0x1a4f03c)
****************************
--- loop:(const auto&)
#1 25 (address=0x1a4f030)
#1 35 (address=0x1a4f034)
#1 45 (address=0x1a4f038)
#1 55 (address=0x1a4f03c)
---------------
--- loop:(index) + 'const auto& ... at()'
#2 6.05888e-38 (address=0x1a4f030)
#2 0 (address=0x1a4f034)
#2 45 (address=0x1a4f038)
#2 55 (address=0x1a4f03c)
---------------
---------------
*** f-closest0(33)=0 (address=0x1a4f034)
Upvotes: 0
Views: 92
Reputation: 7294
@juanchopanza correctly points out that a_vec()
is returning by value, but that doesn't explain the output completely. The issue is that
const auto& af = a.a_vec().at(i);
std::cout << "#" << tryN << " " << af << " (address=" << &af << ")\n";
The first line binds a reference to a value that was returned from a temporary copy of the data, which then goes out of scope, so by the time you access af
again, the underlying memory has been reclaimed and may have been overwritten.
The reason the const reference doesn't extend the lifetime of the temporary is that it is a reference to a result of using the temporary in the expression, not a reference to the temporary itself. If you instead did
const auto &vec = a.a_vec();
std::cout << "#" << tryN << " " << vec.at(i) << " (address=" << &vec.at(i) << ")\n";
then it should be OK. (Though the right answer is to have a_vec
return a const ref to the vector in the first place.)
Upvotes: 2
Reputation: 107
for(unsigned int i=0; i<a.a_vec().size(); ++i){
const &auto af = a.a_vec().at(i);
std::cout << "#" << tryN << " " << af << " (address=" << &af << ")\n";
}
for #2, Remove the & before auto and it will work the same. Your getting a Value not a reference in the second loop!
const auto af = a.a_vec().at(i);
Upvotes: 0
Reputation: 4770
I think this statement is suspect:
const auto& af = a.a_vec().at(i);
I believe the std::vector returned by a.a_vec() goes out of scope after the statement ends. Yet you still have a reference to one of it's members.
You can fix this by taking a reference to the entire vector like this:
const auto & av = a.a_vec();
Or copying an element like this:
const auto af = a.a_vec().at(i);
Upvotes: 0
Reputation: 86
The problem is in the second loop
for(unsigned int i=0; i<a.a_vec().size(); ++i){
const auto& af = a.a_vec().at(i);
std::cout << "#" << tryN << " " << af << " (address=" << &af << ")\n";
}
It's because of the declaration of the variable, const auto&
. If you were to remove the ampersand and hold it by value instead of by reference, it works, so just declare it as const auto
Upvotes: 0
Reputation: 227468
A::a_vec()
returns by value. You get a new vector each time you call it. You could fix this by returning a reference
const std::vector<float>& a_vec() const { return a_vec_; }
or making a_vec_
public and accessing it directly.
Upvotes: 2