Database queries may involve questions of a type you couldn't think of in advance when writing the database program, so some way to dynamically access parts of an object may be needed: struct animal { string name, species; int weight, price; string get(string attribute) { if (attribute == "name") return name; else if (attribute == "species") return species; else if (attribute == "weight") return int_to_string(weight); else if (attribute == "price") return int_to_string(price); cerr << "error - no such thing as animal::" << attribute << "\n"; return ""; } ... etc ... }; given of course string int_to_string(int n) { string s = ""; if (n == 0) return "0"; while (n > 0) { s += (char)(n%10 + '0'); n /= 10; } for (int i=0, j=s.length()-1; idata->get(attribute), value)) return p; return NULL; } OR, with some clever trickery: const animal * not_found = NULL; const animal * first_in_list = (animal *)1; Link * find_previous_link(bool tester(string, string), string attribute, string value) { Link * curr = first, * prev = NULL; while (curr != NULL) { if (tester(p->get(attribute), value)) { if (prev == NULL) return first_in_list; else return curr; } curr = curr->next; } return not_found; } giving animal * find(bool tester(string, string), string attribute, string value) { Link * p = find_previous_link(tester, attribute, value); if (p == not_found) return NULL; else if (p == first_in_list) return first->data; else return p->next->data; } void remove(bool tester(string, string), string attribute, string value) { Link * p = find_previous_link(tester, attribute, value), if (p == not_found) return; Link * old = p->next; else if (p == first_in_list) first = old->next; else p->next = old->next; delete old; }