dereferencing iterator of STL set - c++

I'm having a problem with inheriting from STL set (i think):
Here is class Prime:
class Prime : public set<A> {
private:
// private data members.
public:
// C'tor...
void printParticularA(const int& id);
}
Here is class A:
class A : public List<B>{
private:
// data members.
int id;
public:
// C'tor
A(const A& copy) : List<B>(copy), //copy data members
{ // validate data and throw exceptions if needed. };
bool operator< (const A& rhs) const{
return id < rhs.id;
}
void printReport() const {
for(const B& item : *this){ item.print(); }
}
}
now here is the problem. in the next function i want to print a particular A object in the set:
void Prime::printParticularA(const int& id) {
find(AFinder(id))->printReport();
}
i also tried this:
void Prime::printParticularA(const int& id) {
*(find(AFinder(id))).printReport();
}
note: assume that class B has print() method.
note2: AFinder is a class for making dummy A objects using only the id data.
the problem is that when 'find' finds the objects it returns const_iterator (because every object in set is const), and when i dereference it i get a copy of the object (??) but the list of B inside it is empty!
this happens also for the '->' version.
now i know that set doesn't allow me to change the objects but i do not intend to change the object (as you can see in the declaration of printReport member function).
i appreciate any help in this!
EDIT: thanks everyone, you have helped me a lot especially learning what not to do.
I solved the problem and it wasn't in set, list nor any of my classes presented here.
my mistake was in understanding the question i was given (yes this is my homework assignment, i'm still new to c++).
sorry if you feel i have wasted your time.
i hope that i can learn from all of your experience and someday help others!
in short, THANKS!! :)

Your code is pretty much messed up all around. And the problem does not look tied directly to set or iterator, but general bad things done.
For starters make your op< const and delete the copy ctor for good -- the stock one shall work fine. Use internal find of set to search and look if it found an item.
All that will likely make your described problem gone, if not, post a complete compileable example with proper text of what you see and what you expect.

Your code really violate many rules of C++. Why don't you try smth like this:
#include <iostream>
#include <list>
#include <map>
using namespace std;
class SimpleInt {
public:
int data_;
SimpleInt(const int data): data_(data) {};
void print() const {cout << data_ << " ";};
};
template <typename T>
class A {
private:
// private data members.
public:
list<T> list_of_B_; // this is for simlicity. Make getters as you need
const int id_; // delete it or copy to map. You sholdn't change it.
A(int id) : list_of_B_(), id_(id) {}
A(const A<T>& copy) : list_of_B_(copy.list_of_B_), id_(copy.id_) {} //copy data members
A(A<T>&& copy) : list_of_B_(::std::move(copy.list_of_B_)), id_(copy.id_) {} //move data members
void printReport() const {
for(const T& item : list_of_B_){ item.print(); }
}
};
template <typename T>
class Prime {
private:
// private data members.
public:
// The main difference with your source
map<int, T> map_of_A_; // this is for simlicity. Make getters as you need
// C'tor...
void printParticularA(const int& id) {
auto it = map_of_A_.find(id);
if (it != map_of_A_.end())
it->second.printReport();
}
};
int _tmain(int argc, _TCHAR* argv[])
{
typedef A<SimpleInt> ASimpled;
Prime<ASimpled> prime;
ASimpled a(1);
a.list_of_B_.push_back(SimleInt(1));
a.list_of_B_.push_back(SimleInt(2));
a.list_of_B_.push_back(SimleInt(3));
ASimpled b(2);
b.list_of_B_.push_back(SimleInt(10));
b.list_of_B_.push_back(SimleInt(20));
b.list_of_B_.push_back(SimleInt(30));
prime.map_of_A_.insert(make_pair(a.id_, a));
prime.map_of_A_.insert(make_pair(b.id_, b));
prime.printParticularA(2);
return 0;
}

Although you haven't included the implementation of List the problem is likely there. To be more precise the begin() and end() member functions of List may be broken. Chances are the values they return are identical (or invalid) resulting in the range based for loop doing nothing. This of course is based on your set::find is returning a valid iterator and not the end iterator.
The following example is a modification of the code in your question. It uses std::list instead of List and doesn't use AFinder since you haven't included the code for it.
#include <set>
#include <list>
#include <iostream>
struct B
{
int id_;
explicit B(int id) : id_(id) {}
void print() const
{
std::cout << "B::id = " << id_ << std::endl;
}
};
class A : public std::list<B>
{
public:
explicit A(int id) : id_(id) {}
bool operator<(const A& rhs) const
{
return id_ < rhs.id_;
}
bool operator==(const A& other) const
{
return id_ == other.id_;
}
void printReport() const
{
for(auto& item : *this)
{
item.print();
}
}
private:
// data members.
int id_;
};
class Prime : public std::set<A>
{
public:
void printParticularA(const int& id)
{
std::cout << "finding " << id << std::endl;
auto el = find(A(id));
if(el == cend())
{
std::cout << "not found" << std::endl;
}
else
{
find(A(id))->printReport();
}
std::cout << "done finding " << id << std::endl;
}
};
int main()
{
Prime p;
A a1(1);
a1.push_back(B(1));
a1.push_back(B(2));
a1.push_back(B(3));
p.insert(a1);
A a2(2);
a2.push_back(B(4));
a2.push_back(B(5));
a2.push_back(B(6));
p.insert(a2);
p.printParticularA(1);
p.printParticularA(2);
// doesn't exit
p.printParticularA(3);
}
This produces the following output.
finding 1
B::id = 1
B::id = 2
B::id = 3
done finding 1
finding 2
B::id = 4
B::id = 5
B::id = 6
done finding 2
finding 3
not found
done finding 3

Related

C++ : Getting random symbols as output, not sure why [duplicate]

This question already has answers here:
What is a dangling reference? [duplicate]
(1 answer)
What is a dangling pointer?
(7 answers)
Closed 5 days ago.
I've been going over my code and fiddling but I can't seem to figure out why I'm not getting the expected output but instead random symbols.
The expected output is: JoeUPSReminderPick up your package!
54.23
I get the < but anything after that is gibberish. Any help would be appreciated.
#include <cstddef> // for std::size_t
#include <iostream>
#include <memory>
#include <ostream>
#include <string>
#include <utility> // for std::move, std::forward
#include <vector>
class xml_node_base
{
public:
virtual ~xml_node_base() = default;
void output(std::ostream& os) const
{
do_output_open(os);
do_output_body(os);
do_output_close(os);
}
protected:
virtual void do_output_open(std::ostream& os) const = 0; // abstract
virtual void do_output_body(std::ostream&) const { } // not abstract
virtual void do_output_close(std::ostream& os) const = 0; // abstract
};
using xml_node_base_t = std::shared_ptr<xml_node_base>;
using xml_node_bases_t = std::vector<xml_node_base_t>;
template <typename T, typename... Args>
inline xml_node_base_t make_xml_node(Args&&... args)
{
return std::make_shared<T>(std::forward<Args>(args)...);
}
class xml_node: virtual public xml_node_base
{
private:
std::string const& node_name;
public:
xml_node() = delete;
xml_node(std::string const& name) : node_name(name)
{
};
protected:
void do_output_open(std::ostream& os) const override
{
os << "<" << node_name << ">";
};
void do_output_close(std::ostream& os) const override
{
os << "</" << node_name << ">";
};
};
class xml_node_with_children: public xml_node
{
private:
xml_node_bases_t children_;
public:
xml_node_with_children() = delete;
xml_node_with_children(std::string const& name) : xml_node(name)
{
};
xml_node_with_children(std::string const& name, std::size_t reserve) : xml_node_with_children(name)
{
children_.reserve(reserve);
};
xml_node_with_children(std::string const& name, xml_node_bases_t children) : xml_node(name), children_(std::move(children))
{
};
protected:
auto& children() { return children_; };
auto const& children() const { return children_; };
void do_output_body(std::ostream& os) const
{
for (auto const& c : children_)
{
c -> output(os);
}
};
};
template <typename T>
class value_node : public xml_node
{
private:
T datum;
protected:
void do_output_body(std::ostream& os) const
{
os << datum;
}
public:
value_node(std::string const& name, T const& v) : xml_node(name), datum(v)
{
}
};
class note : public xml_node_with_children
{
public:
note() = delete;
note(std::string const& to, std::string const& from, std::string const& subject, std::string const& message) : xml_node_with_children("note", 4)
{
children().push_back(make_xml_node<value_node<std::string>>("to",to));
children().push_back(make_xml_node<value_node<std::string>>("from",from));
children().push_back(make_xml_node<value_node<std::string>>("subject",subject));
children().push_back(make_xml_node<value_node<std::string>>("message",message));
}
};
class root : protected xml_node_with_children
{
public:
using xml_node_with_children::xml_node_with_children;
using xml_node_with_children::output;
using xml_node_with_children::children;
};
std::ostream& operator<<(std::ostream& os, root const& r)
{
r.output(os);
return os;
}
int main()
{
root notes{"notes"};
notes.children().push_back(
make_xml_node<note>("Joe", "UPS", "Reminder", "Pick up your package!")
);
notes.children().push_back(
make_xml_node<value_node<double>>("priority",54.23)
);
std::cout << notes << '\n';
}
I think the problem could be with the for loop on line 90, since I'm not too familiar with the -> operator.
std::string const& node_name;
xml_node(std::string const& name) : node_name(name)
This class member is a reference, and the constructor initializes it from a reference that gets passed in as a parameter to the constructor.
Let's trace things all the way back and see where the parameter, to the constructor, originally comes from. Here's one example:
children().push_back(make_xml_node<value_node<std::string>>("to",to));
The parameter is a literal string, "to".
C++ is very famous, and is very well known for giving everyone every opportunity to shoot themself in the foot, if that's what they really want to do, so:
A temporary std::string object gets constructed.
A reference to this object gets passed as a parameter, through several onion layers of constructors, elephant-style.
A reference to this object gets saved in a member of the base class.
After all the constructors finish, and this statement finishes executing, the temporary std::string object, that owns this "to" gets destroyed.
The instance of the class now has a reference to a destroyed object, in its node_name.
This is repeated for all the other objects in the shown code that get constructed like that.
You just shot yourself in the foot.

How can I maintain polymorphism when creating an instance on stack in C++?

To create instance on the heap and maintain polymorphism, and that'll give the right answer:
class Father
{
public:
virtual void Say()
{
cout << "Father say hello" << endl;
}
};
class Son : public Father
{
public:
void Say()
{
cout << "Son say hello" << endl;
}
};
int main()
{
std::vector<Father*> v;
std::cout << 1 << std::endl;
for(int i(0); i<5; i++)
{
auto p = new Son(); ---------------on heap
v.emplace_back(p);
}
for(auto p : v)
{
p->Say();
}
}
But when I want to create an instance on the stack, it seems not so easy:
Edition 1:
class Father
{
public:
virtual void Say()
{
cout << "Father say hello" << endl;
}
};
class Son : public Father
{
public:
void Say()
{
cout << "Son say hello" << endl;
}
};
int main()
{
std::vector<Father> v;
for(int i(0); i<5; i++)
{
auto o = Son(); ---------------on stack
v.emplace_back(o);---------------now "o" is cast to Father type
}
for(auto o : v)
{
o.Say();------------------------only output "Father say hello"
}
}
And edition 2:
class Father
{
public:
virtual void Say()
{
cout << "Father say hello" << endl;
}
};
class Son : public Father
{
public:
void Say()
{
cout << "Son say hello" << endl;
}
};
int main()
{
std::vector<Father*> v;
for(int i(0); i<5; i++)
{
auto p = &Son(); --------------On the stack
v.emplace_back(p);---------------Now "o" is cast to Father type
}
for(auto p : v)
{
p->Say();------------------------Since "p" now is a Wild pointer, it'll fail too
}
}
Can this be fixed? Or is it just a dead end: If I want to use polymorphism, then I have to create an object on the heap.
In general polymorphism does not require dynamic allocations. That's a common misunderstanding, and hence here comes a counter example:
void foo(const Father& f) { f.Say(); }
Son s;
foo(s);
You have to declare Say as const to make it work, but then it will print the expected Son say hello. You need references or pointers for polymorphism, not necessarily dynamic allocation!
Having said this, when you want a container of derived classes, then std::vector<Father> won't do. Public inheritance models a "is-a" relation, so a Son is a Father, but a Father is not a Son (notice how wrong and misleading the father-son analogy is?!?). Hence when you put a Son into a vector of Fathers, then the object gets sliced and only the Father part is stored in the vector (read about "object slicing").
Moreover, auto p= &Son(); is wrong, because the created object is temporary and its life time ends at the end of that line. The pointer you store in the vector is dangling (it points to an object whose lifetime already ended).
To store pointers in the container you can use dynamic allocations. For example, with std::unique_ptrs:
int main()
{
std::vector<std::unique_ptr<Father>> v;
for(int i(0);i<5;i++){
v.emplace_back(new Son);
}
for(auto& p:v){
p->Say();
}
}
Note that you have to use auto& for the range based for loop, because unique_ptr doesn't copy. unique_ptr does the dirty work: the objects will get deleted automatically when the unique_ptrs get destroyed (which is when the vector goes out of scope).
This is a recurring problem/dilemma: you can maintain a value semantic at the expense of some boilerplate code. Here is a minimal working example of such kind of idea:
#include <iostream>
#include <memory>
#include <vector>
class Father
{
protected:
struct Father_Interface
{
virtual void
Say() const
{
std::cout << "Father say hello" << std::endl;
}
};
using pimpl_type = std::shared_ptr<const Father_Interface>;
pimpl_type _pimpl;
Father(const Father_Interface* p) : _pimpl(p) {}
public:
Father() : Father{new Father_Interface{}} {}
void Say() const { _pimpl->Say(); }
};
class Son : public Father
{
protected:
class Son_Interface : public Father_Interface
{
void
Say() const override
{
std::cout << "Son say hello" << std::endl;
}
};
public:
Son() : Father{new Son_Interface{}} {}
Son& operator=(const Father&) = delete; // fight against object slicing
};
int
main()
{
std::vector<Father> v;
v.emplace_back(Father());
v.emplace_back(Son());
v.emplace_back(Father());
for (const auto& v_i : v)
{
v_i.Say();
}
}
which prints:
Father say hello
Son say hello
Father say hello
You can also read about:
Sean Parent's better-code-runtime-polymorphism
discussion about its usage
There are lots of things you are doing wrong. First here is how you can do it right:
int main()
{
Father father1;
Son son1;
Father father2;
Son son2;
std::vector<Father*> v;
v.emplace_back(&father1);
v.emplace_back(&son1);
v.emplace_back(&father2);
v.emplace_back(&son2);
for (auto p : v) {
p->Say();
}
}
Basically you need to allocate the objects on the stack so that the objects will be available for as long as the vector is.
Now what you did is undefined behavior, because you basically had pointers (in the vector) to objects that were already de-allocated (even if you fix what was already said in the comments).
for(int i(0);i<5;i++){
Son s;
v.emplace_back(&s);
// s lifetime end here, but the vector still has pointers to objects that are de-allocated
}
For this bit: v.emplace_back(p);---------------now "o" is cast to Father type.
I think you tried something completely different: a vector of Father objects std::vector<Father> and if you try to add Son elements into that you get object slicing // I just added this bit so that you can look it up, this is not the main point here
The question doesn't have much to do with stack. You're really asking how to implement polymorphism when storing by value. It's not too hard if you can use C++17 and thus have std::variant available.
The implementation is surprisingly simple:
#include <algorithm>
#include <cassert>
#include <variant>
#include <vector>
enum class Who { Father, Son };
struct ISayer {
virtual Who Me() const = 0;
virtual ~ISayer() {};
};
struct Father final : ISayer {
Who Me() const override { return Who::Father; }
};
struct Son final : ISayer {
Who Me() const override { return Who::Son; }
};
struct AnySayer0 : std::variant<Father, Son>
{
using variant_type = std::variant<Father, Son>;
using variant_type::variant;
operator const ISayer &() const {
return std::visit([](auto &val) -> const ISayer &{ return val; },
static_cast<const variant_type &>(*this));
}
operator ISayer &() {
return std::visit([](auto &val) -> ISayer &{ return val; },
static_cast<variant_type &>(*this));
}
const ISayer *operator->() const { return &static_cast<const ISayer &>(*this); }
ISayer *operator->() { return &static_cast<ISayer &>(*this); }
};
using AnySayer = AnySayer0;
int main()
{
std::vector<AnySayer> people;
people.emplace_back(std::in_place_type<Father>);
people.emplace_back(std::in_place_type<Son>);
assert(people.front()->Me() == Who::Father);
assert(people.back()->Me() == Who::Son);
}
An alternate implementation AnySayer1 would need a bit more boilerplate, and perhaps be a bit faster - but would it be a bit smaller as well?
struct AnySayer1
{
template <typename ...Args>
AnySayer1(std::in_place_type_t<Father>, Args &&...args) :
father(std::forward<Args>(args)...), ref(father) {}
template <typename ...Args>
AnySayer1(std::in_place_type_t<Son>, Args &&...args) :
son(std::forward<Args>(args)...), ref(son) {}
~AnySayer1() { ref.~ISayer(); }
operator const ISayer &() const { return ref; }
operator ISayer &() { return ref; }
const ISayer *operator->() const { return &static_cast<const ISayer &>(*this); }
ISayer *operator->() { return &static_cast<ISayer &>(*this); }
AnySayer1(AnySayer1 &&o) : ref(getMatchingRef(o)) {
if (dynamic_cast<Father*>(&o.ref))
new (&father) Father(std::move(o.father));
else if (dynamic_cast<Son*>(&o.ref))
new (&son) Son(std::move(o.son));
}
AnySayer1(const AnySayer1 &o) : ref(getMatchingRef(o)) {
if (dynamic_cast<Father*>(&o.ref))
new (&father) Father(o.father);
else if (dynamic_cast<Son*>(&o.ref))
new (&son) Son(o.son);
}
AnySayer1 &operator=(const AnySayer1 &) = delete;
private:
union {
Father father;
Son son;
};
ISayer &ref;
ISayer &getMatchingRef(const AnySayer1 &o) {
if (dynamic_cast<const Father *>(&o.ref))
return father;
if (dynamic_cast<const Son *>(&o.ref))
return son;
assert(false);
}
};
This could be rewritten using the same "magic" that makes std::variant work - it'd be less repetitive that way.
But - is it smaller?
static_assert(sizeof(AnySayer1) == sizeof(AnySayer0));
No. At least in both gcc and clang, both implementations have the same size. And that makes sense, since std::variant doesn't need to store any more information than we do - it only needs to keep some way to discriminate the type. We chose to use the reference to ISayer and use dynamic type information, since that optimizes for the common case when we convert to the interface type - we store the reference ready to use. std::variant can't assume that the types have a common base, and instead stores an integer type index instead and uses generated code to dispatch on that index. It may be generally a bit slower in the path that uses the visitor to return the reference - but not necessarily, since the compiler can note that both types have their ISayer vtable pointer at the same location, and can squash the type-based dispatch down to a "has value vs has no value" test. It seems that the most recent versions of all major C++ compilers (gcc, clang and MSVC) easily deal with this and generate code that is just as fast as our "optimized" AnySayer1.

CRTP vs. vector of base class

I have an issue similar to this C++ vector of CRTP shared pointers but my problem formulation adds the fact that the return type of a function i want to use for all inheriting classes is templated.
In detail lets assume this :
template <class Derived>
class Base {
Derived Value() const {
return static_cast<Derived>(this->Value());
};
};
class ChildDouble : public Base<ChildDouble> {
public:
ChildDouble(double r) : _value(r){};
double Value() const {
return _value;
};
private:
double _value;
};
class ChildString : public Base<ChildDouble> {
public:
ChildString(string s) : _value(s){};
string Value() const {
return _value;
};
private:
string _value;
};
Goal would be to use it somewhat similar as in the following main
void main() {
std::vector<Base*> vec;
vec.push_back(new ChildDouble(3.0));
vec.push_back(new ChildString("Thomas"));
unsigned counter = 0;
for (const auto& e : vec) {
std::cout << "Entry " << counter << " : " << e->Value()
<< std::endl;
counter++;
}
}
The compiler is obviously not happy with this because Base requires a template argument.
Any Ideas how this could be solved? AM I using CRTP here although i should not be using it?
Virtual methods (which is what you'd normally need to get the above working without CRTP) won't work here because the interface is different for Value() in each derived type. Virtual inheritance depends on the signature being the same for everyone, except in a few special cases like with covariant return types. It also won't work because virtual methods can't be templated.
But, you can use std::variant to dynamically dispatch your incompatible interfaces, because it is based on templates. First, define a convenient alias for your variant:
using Child = std::variant<ChildDouble, ChildString>;
And then to use, dispatch with std::visit and a generic lambda:
std::vector<Child> vec;
vec.push_back(ChildDouble(3.0));
vec.push_back(ChildString("Thomas"));
unsigned counter = 0;
for (const auto& e : vec) {
std::visit([&counter](auto&& v) {
std::cout << "Entry " << counter << " : " << v.Value()
<< std::endl;
}, e);
counter++;
}
Demo: https://godbolt.org/z/bENWYW
It doesn't work because the compiler doesn't know which type you want to put in the vector and you need to specified it. If you try vector<Base<double>*>vec; it will works but you can't use the vector with other types like Base, because, it is other type.
The solution is to use std::variant or std::any in place of template.
Now you have an object variant/any the declare value in base will make your life easier.
Also I suggest you:
not to use variables starting with underline '_' because this syntax is used by many internal function of compiler.
not to use raw pointer. use smart_ptr like share_ptr then you don't need to worry to destroy it with delete.
Below the code with the changes:
#include <memory>
#include <vector>
#include <string>
#include <variant>
#include <iostream>
using namespace std;
struct Base {
Base(variant<double, string> val) : value(val) {}
void Print() { //just to ilustrate how it works. Better use ostream
if (holds_alternative<double>(this->value))
cout << get<double>(this->value);
else if (holds_alternative<string>(this->value))
cout << get<string>(this->value);
}
protected:
variant<double, string> value;
variant<double, string> BaseValue() const { return this->value; };
};
struct ChildDouble : public Base {
ChildDouble(double r) : Base(r) {};
double Value() const { return get<double>(this->BaseValue()); }
};
struct ChildString : public Base {
ChildString(string s) : Base(s) {};
string Value() const { return get<string>(this->BaseValue()); };
};
int main() { //must return int not void
vector<shared_ptr<Base>>vec;
vec.emplace_back(new ChildDouble(3.0));
vec.emplace_back(new ChildString("Thomas"));
unsigned counter = 0;
for (const auto& e : vec) {
cout << "Entry " << counter << " : "; e->Print(); cout << endl;
++counter;
}
}

Printing all elements of multimap, which are pair of 2 different objects in C++?

I have 2 classes A and B. I create objects which are then put into a multimap. I want to print the all the keys with their corresponding values. However, my attempt to do this was not so successfull as I could create an iterator. I would like to ask how can I use the equal_range() method to accomplish this. Thanks in advance!
#include "pch.h"
#include <iostream>
#include <map>
using namespace std;
class A {
int key;
public:
A(int k) {
key = k;
}
A(A ob) {
key = ob.key;
}
int getKey() {
return key;
}
};
class B {
string value;
public:
B(string v) {
value = v;
}
};
multimap <A, B> mp;
int main() {
mp = {
make_pair(A(1),B("Crime and punishment")),
make_pair(A(1),B("The Idiot")),
make_pair(A(1),B("Brothers' Karamazov")),
make_pair(A(2),B("Andreshko")),
make_pair(A(2),B("The Gerak family")),
make_pair(A(3),B("The name of the rose")),
make_pair(A(3),B("Baudolino"))
};
for (auto ml = mp.begin(); ml != mp.end();ml++) {
multimap<pair<int, string>, pair<int, string>>::iterator it;
}
}
You already have your loop, but let's use C++11 ranged-loop instead.
You are just missing an accessor for the value, so let's assume that you have it (getValue) and just access the iterator:
for (const auto& pair : mp) {
std::cout << ml.first.getKey() << "\t" << ml.second.getValue() << std::endl;
}
Also change this:
A(A ob)
To
A(const A& ob)
This will give a real copy assignment. But the default copy constructor is also fine, so don't mention it at all, the default one is already good for you.
Some things you need:
Getter methods for class B.
A less than operator for your key class A.
A copy constructor for class A.
In the code below if remove code not needed (but would be nice to keep in live code) to show what is really used.
#include <iostream>
#include <map>
#include <string>
using namespace std;
class A {
int key;
public:
A(int k) : key(k){}
A(A const& ob) : key(ob.key) {}
A& operator=(A const& ob) = delete;
int getKey() const { return key; }
friend bool operator<(A const&left, A const&right) { return left.key < right.key; }
};
class B {
string value;
public:
B(string const& v) : value(v) {}
B(B const&) = default;
B& operator=(B const&) = delete;
string const& getValue() const { return value; }
};
multimap<A, B> mp;
int main() {
mp = {
make_pair(A(3), B("Baudolino")),
make_pair(A(1), B("Crime and punishment")),
make_pair(A(1), B("Brothers' Karamazov")),
make_pair(A(2), B("Andreshko")),
make_pair(A(1), B("The Idiot")),
make_pair(A(2), B("The Gerak family")),
make_pair(A(3), B("The name of the rose"))
};
for (auto const & item : mp) {
cout << "key:" << item.first.getKey() << " value:\"" << item.second.getValue() << "\"\n";
}
}

[List / Vector]: Need some foundation advice

I would like some advice on how I can solve an interesting problem I have.
The problem is to have two storage containers, of which the user selects which one to use for the remainder of the program (edit: at runtime). The two containers are Vector and List and store an object type we are to define. These two containers can be accessed using any means you desire (pop/[i]/...) How would you go about solving this problem?
Below is my best (almost working) solution, but I would really like to see what solutions more skilled C++ professionals have. As previously stated, I am really interested if I am taking the right approach. I have more than typical free time this semester and I intend to use it to really improve my c++ abilities. Thanks for your feedback.
Version 1
To start, I have a boolean flag,
bool using_vector = true; // what storage container was selected?
Second comes my two containers,
list<Question> q_llist;
vector<Question> q_vec;
Third my implementations for accessing the containers,
(still haven't figured out how make get_question() work in a graceful way, and I am not fond of the current route I am taking at the moment)
const Question& get_question(){
Question q = (using_vector) ?
q_vec.back() : q_llist.back();
(using_vector) ?
q_vec.pop_back() : q_llist.pop_back();
return q;
}
int questions_size(){
return (using_vector) ?
q_vec.size() : q_llist.size();
}
void push_back_question(Question& q){
if(using_vector){
q_vec.push_back(q);
}else{
q_llist.push_back(q);
}
}
Version 2
Note: Please use the tag "#v2" when referencing.
I decided to attempt the polymorphism approach. How does this implementation look?
/**
* using polymorphism to implement a parent class "Container"
* depending on user selection, reference C_Vec or C_List
*/
class Container {
protected:
list<Question> qlist;
vector<Question> qvec;
public:
void push_back(Question& q){/** do nothing */}
void pop_back(){/** do nothing */}
int size(){/** do nothing */}
Question& back(){/** do nothing */}
};
class C_Vec: public Container{
public:
void push_back(Question& q){qvec.push_back(q);}
void pop_back(){qvec.pop_back();}
int size(){return qvec.size();}
Question& back(){return qvec.back();}
};
class C_List: public Container{
public:
void push_back(Question& q){qlist.push_back(q);}
void pop_back(){qlist.pop_back();}
int size(){return qlist.size();}
Question& back(){return qlist.back();}
};
int main(){
Container *store;
char user_in;
cout << "Before we begin please select a storage container:" << endl
<< "a) Linked List" << endl
<< "b) Vector" << endl << ':';
cin >> user_in;
if(tolower(user_in) == 'a'){
C_List l;
store = &l;
}else{
C_Vec v;
store = &v;
}
}
You have several options. If you need to decide at runtime which container to use, polymorphism (inheritance) might work well.
#include <vector>
#include <list>
#include <memory>
struct Question {};
// runtime
struct Question_container {
virtual const Question& get_question() = 0;
virtual int questions_size() = 0;
virtual void push_back(const Question&) = 0;
virtual ~Question_container() = default;
};
struct Vector_question_container : Question_container {
const Question& get_question() override { return qv.back(); }
int questions_size() override { return qv.size(); }
void push_back(const Question& q) override { qv.push_back(q); }
private:
std::vector<Question> qv;
};
struct List_question_container : Question_container {
const Question& get_question() override { return qv.back(); }
int questions_size() override { return qv.size(); }
void push_back(const Question& q) override { qv.push_back(q); }
private:
std::list<Question> qv;
};
int main()
{
// some how figure out which container to use
std::unique_ptr<Question_container> qc{new Vector_question_container()};
}
If you can make the choice at compile-time, you could make the underlying sequence a template (or even template template) argument.
// CompileTime
template<typename Sequence>
struct Question_container_c {
const Question& get_question() { return s.back(); }
int questions_size() { return s.size(); }
void push_back(const Question& q) { s.push_back(q); }
private:
Sequence s;
};
int main()
{
Question_container_c<std::list<Question>> qlc;
Question_container_c<std::vector<Question>> qvc;
return 0;
}
Although you could also just make your algorithm work on iterators and leave the choice of the container to the user. This might be hard for some methods such as your push_back, but it doesn't actually do anything else then the normal push_back already provided.
To complement #pmr's answer, if you want to do it in an idiomatic way, you can create an adapter interface:
class IContainer {
public:
virtual ~IContainer() {}
virtual void push_back(const Question & q) = 0;
virtual void pop_back() = 0;
virtual const Question & back() const = 0;
virtual unsigned int size() const = 0;
};
And a generic implementation:
template <class T>
class Container: public IContainer {
private:
T m_container;
public:
virtual void push_back(const Question & q) {
m_container.push_back(q);
}
virtual void pop_back() {
m_container.pop_back();
}
virtual const Question & back() const {
return m_container.back();
}
virtual unsigned int size() const {
return m_container.size();
}
};
So you can do this:
std::unique_ptr<IContainer> pctr;
if (choice) {
pctr.reset(new Container<std::vector<Question>>);
}
else {
pctr.reset(new Container<std::list<Question>>);
}
std::cout << pctr->size();
I suppose the best way for your approach is to use iterators instead. Iterators are invented as a container abstraction in mind (sure thing, you can't abstract by 100% due different behavior of containers but anyway you have a solution better than nothing).