views:

96

answers:

3

I was expecting the second assert in the following to pass. I'm asking for your help.

Edit: It didn't work when I had poss everywhere instead of poss_a in some places.

#include <vector>
#include <cassert>

class Sampler
{
public:
 std::vector<int*> poss;
 std::vector<int*>::const_iterator poss_it;
 Sampler(std::vector<int*> poss_a) : poss(poss_a), poss_it(poss.begin())
 {
  assert( (poss[0]) == (*poss_it) ); //passes
 }
};

int main()
{
 int someInt;
 std::vector<int*> poss_a(1, &someInt);
 Sampler sampler(poss_a);
 assert( ((sampler.poss)[0]) == (*(sampler.poss_it)) ); //passes now

 return 0;
}
+1  A: 

Probably getting confused over the local variables overriding the instance variables, try

#include <vector>
#include <cassert>

class Sampler
{
public:
    std::vector<int*> poss;
    std::vector<int*>::const_iterator poss_it;
    // THIS IS THE LINE I CHANGED
    Sampler(std::vector<int*> aPoss)    : poss(aPoss), poss_it(poss.begin())
    {
        assert( (poss[0]) == (*poss_it) ); //passes
    }
};

int main()
{
    int someInt;
    std::vector<int*> poss(1, &someInt);
    Sampler sampler(poss);
    assert( ((sampler.poss)[0]) == (*(sampler.poss_it)) ); //fails

    return 0;
}
Lou Franco
That's right, so why does the second assert fail?
Because this->poss_it is an iterator into the argument and this->poss is a copy of the argument. In the working assert, you are comparing the iterator to the arguments result from indexing, not this->poss. Later you are comparing the iterator to this->poss. It's confusing to explain because you use the variable name poss in three places -- in the constructor, the argument is hiding the member.
Lou Franco
+1  A: 

The poss_it(poss.begin()) line in your initializer list is pulling an iterator from the poss that is the input to the constructor, not the one that is a field of the class.

This is a scope issue. When you're in the constructor, it's fine, because your iterator is a pointer to the input to the function. When you leave the constructor however, it's pointing to the same spot in memory but now you have no idea what's at that location.

You should change the name of the input variable in the constructor. You want one that does this:

Sampler(std::vector<int*> input) : poss(input), poss_it(poss.begin())

Whereas what your current one is doing is this:

Sampler(std::vector<int*> input) : poss(input), poss_it(input.begin())
Alex Zylman
+1  A: 

You are essentially invoking undefined behavior because sampler.poss_it is an iterator into a std::vector<int*> object that was destructed before the second assert was executed.

The problem was on the line:

Sampler(std::vector<int*> poss) : poss(poss), poss_it(poss.begin())

where the first poss of poss(poss) refers to the Sampler object's poss member, the second poss of poss(poss) refers to the poss parameter, and the poss of poss_it(poss.begin()) refers again to the poss parameter (not the poss member). The poss parameter goes out of scope at the end of the constructor, so the poss parameter is destructed, meaning that the poss_it iterator is no longer valid.

To avoid this type of problem, C++ programmers almost always avoid shadowing variables. Also, a copy constructor should probably take a const-reference (to avoid pass-by-value for potentially large objects):

Sampler(const std::vector<int*>& poss_) : poss(poss_), poss_it(poss.begin())
Daniel Trebbien