views:

247

answers:

2

The following code compiles correctly under VC++ 8 on XPSP3, but running it causes a runtime error.

My header looks like:

#include <stdexcept>
#include <iterator>
#include <list>


template<typename T>
class test_generator
{
    public:
    typedef T result_type;

    //constructor
    test_generator()
    {
     std::generate_n( std::back_inserter( tests ), 100, rand );
     value = tests.begin();
    }

    result_type operator()( void )
    {
     if( value == tests.end() )
     {
      throw std::logic_error( "" );
     }

            return *value++;
    }

    private:

    std::list<T> tests;
    typename std::list<T>::iterator value;

};

My implementation looks like:

#include <functional>
#include <algorithm>
#include <iostream>
#include <deque>

#include "test.h"

int main()
{
    test_generator<double> test;
    std::deque<double> tests;

    std::generate_n( std::back_inserter( tests ), 10, test );

    return 0;
}

This compiles fine, it generates an exception (not the logic_error exception defined in the header).

If I change the implementation to use a function instead of a functor, it works:

int main()
{
    std::deque<int> tests;
    std::generate_n( std::back_inserter( tests ), 10, rand );

    return 0;
}

What's wrong with using a functor here?

A: 

I haven't figured out what's causing the exception so far, but you may wish to have return *value++ in your operator(). :-)

Chris Jester-Young
changing *(value++) to *value++ had no affect.
ceretullis
I'm not saying that *value++ is different from *(value++), just that your sample code above lacked it. But it's good that your actual code (which you should paste, by the way) has it. :-)
Chris Jester-Young
sry about that... this is actually my real code... I just didn't catch that until after I posted.
ceretullis
+4  A: 

The test_generator constructor initialises the value iterator to reference the first element in the tests list (which is a member of test_generator).

When you call std::generate_n, a copy of the test is made (because the object is passed by value). In the copied object, the value iterator refers to the tests list in the original object, not the copy.

Because of the iterator debugging checks performed in the Visual Studio STL implementation, this triggers an assertion, because an iterator obtained from one container should not be compared against an iterator from another container.

To fix the problem, you could either implement a copy constructor for your test_generator class, or defer initialisation of value until the first time operator() is invoked.

ChrisN
Moved the initialization of value into the initialization list, and implemented a copy constructor, this fixed the issue.
ceretullis