tags:

views:

176

answers:

6

Hi,

I'm trying to write a method that gets a vector of strings and returns a pointer to a random element. Can you please tell what is the problem with the following code?

string* getRandomOption(vector<string> currOptions){
    vector<string>::iterator it;
    it=currOptions.begin(); 
    string* res;

    int nOptions = currOptions.size();

    if(nOptions != 1){
        int idx = rand() % (nOptions-1);

        while (idx!=0){
            it++;
            idx--;
        };

    };


    res = &(*it);
};

Thanks, Li

+1  A: 

maybe you want to change your function prototype to:

const string* getRandomOption(const vector<string>& currOptions)

or

string* getRandomOption(vector<string>& currOptions)

or else you just get an element from a temporarily copy.

lz_prgmr
You cannot get a pointer to a mutable string from an immutable vector of strings.
FredOverflow
yea, you are right, corrected.
lz_prgmr
+5  A: 

If you want to "returns a pointer to a random element" you need to pass a reference to your vector. For now, it is copied !

You should do :

string* getRandomOption(vector<string> & currOptions)

By the way there is no return in your function for the moment, you need to add a return statement to send your pointer.

Guillaume Lebourgeois
That should probably be a const reference seeing as currOptions is not modified.
Billy ONeal
+1 @Billy: The OP wanted a string* as result. So, you need a non-const ref. In my opinion this answer could explain a little the problem with respect to dangling pointers.
sellibitze
+1  A: 

You are passing the vector by value, i.e. the function has a local copy of the original vector. Then you [intend to] return a pointer to an element in this vector. But when you return from the function, this local copy is destroyed, and your pointer is left dangling.

AngryWhenHungry
+1 You actually answered the question and explained the problem (unlike others)
sellibitze
+9  A: 

Why return a pointer? Keep it simple!

std::string random_option(const std::vector<std::string>& options)
{
    return options[rand() % options.size()];
}

And since this works for any type, not only strings, I would prefer a generic solution:

template <typename T>
T random_element(const std::vector<T>& options)
{
    return options[rand() % options.size()];
}
FredOverflow
+1 -- but see my answer for the same but with iterators. You could simply implement the vector version of this in terms of the iterator version.
Billy ONeal
This doesn't really answer the question, does it?
sellibitze
@sellibitze: Not really. OTOH it is the "right thing to do". @FredOverflow: What happens when `options.size()` is zero ;)
Billy ONeal
@Billy: How often do you have to randomly chose between zero options? :)
FredOverflow
@FredOverflow: Never, but that doesn't mean the function should explode (or rather enter UB). (Throwing an exception would be okay...)
Billy ONeal
+3  A: 

Better version of the same, because it works with any container, not just vectors:

template <typename ForwardIterator>
ForwardIterator random_element(ForwardIterator begin, ForwardIterator end)
{
    typename std::iterator_traits<ForwardIterator>::difference_type
        size = std::distance(begin, end);
    if (size) //divide by zero errors are bad
        std::advance(begin, std::rand() % size);
    return begin;
}

If you happen to be lucky enough to be using C++0x, you can replace the above with this:

template <typename ForwardIterator>
ForwardIterator random_element(ForwardIterator begin, ForwardIterator end)
{
    auto size = std::distance(begin, end);
    if (size) //divide by zero errors are bad
        std::advance(begin, std::rand() % size);
    return begin;
}

Which gets around the nasty std::iterator_traits<t>::difference_type glue.

Billy ONeal
I really wish someone would tell me why they downvoted this.
Billy ONeal
+1as it demonstrates some good practice and was downvoted without a reason.
lz_prgmr
+1 for idiomatic code
sellibitze
A: 

Aside from the other problems mentioned in the other answers, when a vector resizes, any pointers or iterators (or references of any nature) become invalid. Don't return a pointer.

DeadMG
+1 because some prick downvoted all of us without commenting.
Billy ONeal