2

I've created useful helper method, which looks like this:

template<typename T>
T getRandomItem(vector<T> items){
    if(items.isEmpty()) return nullptr;
    int i = random(0, (int) items.size() - 1);
    return items.at(i);
}

It simply gets random index from vector and return item at that position.

Let me show the first example:

vector<string> v = {"foo", "bar"};
string item = getRandomItem(v);

Compiler doesn't see anything wrong here, but I'm getting very strange linker error:

Undefined symbols for architecture armv7:
  "std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > getRandomItem<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >)", referenced from:

Also this doesn't even pass compiler:

auto v = {"foo", "bar"};
string item = getRandomItem(v);

I'm getting following error:

No matching function for call to 'getRandomItem'

Also it doesn't work if I'd like to use it inline:

string item = getRandomItem({"foo", "bar"});

I tried adding before parameters like this:

string item = getRandomItem<string>({"foo", "bar"});

However then I'm getting another compiler error:

Call to 'getRandomItem' is ambiguous.

How can I fix linker error? How should I change my code to pass vector inline?

edit: This example is being compiled by xcode 8.2.1 and random function is from cocos2d-x library (if it does matter).

edit2: After removing template code started compiling:

string getRandomItem(vector<string> items){
    if(items.size() == 0) return nullptr;
    int i = random(0, (int) items.size() - 1);
    return items.at(i);
}

Also I now can do this:

string item = getRandomItem({"foo", "bar"});

So the main question remains: why xcode compiler doesn't allow me to use template here?

Makalele
  • 7,431
  • 5
  • 54
  • 81
  • Show your build commands (compilation and linking). Also don't cut error messages. Also what exactly do you want `v` to be in `auto v = {"foo", "bar"};` and why? – n. m. could be an AI Feb 08 '17 at 11:06
  • Possibly [related](http://stackoverflow.com/questions/6429494/undefined-symbols-for-architecture-armv7) ? – George Feb 08 '17 at 11:07
  • `{"foo", "bar"}` is not a vector. In order to pass something like it inline, look at [`std::vector` constructors](http://en.cppreference.com/w/cpp/container/vector/vector). Don't pay attention to the copy ctor, look at all the others (as `{"foo", "bar"}` is not a vector). See something interesting? – n. m. could be an AI Feb 08 '17 at 11:11

3 Answers3

2

Well, first things first: {"foo", "bar"} is clearly not automatically interpreted as a string. It is interpreted as a std::initializer_list<const char*>. So there is no function found that has a std::initializer_list<const char*> as input and returns a std::string. So you cannot use auto there, or you should also use auto for the return type "item". But if you combine auto and templates too much, you might end up with all kind of types that you don't want.

But the next line is also asking for problems: if(items.isEmpty()) return nullptr; . According to the C++11 standard, sect. 21.4.2.9, basic_string(const charT* s, const Allocator& a = Allocator()); Requires: s shall not be a null pointer. Better you return T().

The linker issue might be related to your build script. Or in what files you define everything.

JHBonarius
  • 10,824
  • 3
  • 22
  • 41
  • You're right - auto won't work. However after removing template (I just left vector) I can pass {"foo", "bar"} in function call. I also changed isEmpty to size() == 0. See my edit. – Makalele Feb 08 '17 at 11:30
0

i fixed it

#include <vector>
#include <string>
#include <iostream>
#include <time.h>

using namespace std;

template<typename T>
T getRandomItem(vector<T> items) {
if (items.size() == 0) return 0;
int i = rand()%items.size()+0;
return items.at(i);
}

void main() {
srand(time(0));

vector<string> v = { "foo", "bar","test","test2"};
string item = getRandomItem(v);
cout << item<<endl;
}
Martian
  • 99
  • 1
  • 11
  • I'm still getting linker error in xcode. I have to remove template and just leave vector to get it working. – Makalele Feb 08 '17 at 11:30
  • You can't return 0 if items empty. What if 0 is meaningless for type T. `string item = 0;` it might work because of some implicit cast but is not what you mean. `return T();` rather. – acraig5075 Feb 08 '17 at 12:46
  • acraig5075 you are right but it;s not important i fixed the main problem. – Martian Feb 08 '17 at 13:46
0

I am able to compile this piece of code :

#include <iostream>
#include <vector>
#include <cstdlib>
#include <ctime>

using namespace std;

template<typename T>
T getRandomItem(vector<T>& items){ // note that we should pass by reference to prevent extra copy
    if(items.size() == 0) return T();
    srand(time(NULL));
    int i = rand()%items.size()+0;
    return items.at(i);
}

int main() {
    vector<string> v;
    v.push_back("foo");
    v.push_back("bar");
    string item = getRandomItem(v);
    std::cout << item;
    return 0;
}    

See here

Rishi
  • 1,387
  • 10
  • 14