In C++, I have an class which is ordered by its name which is a std::string. I wish to only have one per each unique name in either a std::map or std::set.
I could use a std::set since the operator< will order my instances by their name, however, I need to lookup an instance by its name. Using a map where the key is the name is straight forward, however, I could also use a set and construct a dummy instance of my class with the name I wish to lookup to locate in the set the actual instance of the class for the given name.
I imagine I should just go with the map to make the code straight forward, but wonder if there might be a way to go with the set since the key is effectively part of my object anyway and thus avoid some redundancy.
Is there a way to use the set and be able to locate objects by their key in a clean way or should I just use a map and be done with it?
Here is the class to be inserted (in draft form) and in each directory there is either a set or map of Node(s) keyed off the Node’s name:
class Node {
public:
Node(Directory &parent, const std::string &name)
: _name(name),
_parent(&parent),
_isRoot(false) {
if (name.empty()) {
throw InvalidNodeNameError(name);
}
}
protected:
// This is only used for the root directory:
Node()
: _name(""),
_parent(0),
_isRoot(true) {
}
Node(const std::string &name)
: _name(name),
_parent(0),
isRoot(false) {
}
public:
virtual ~Node() {
if (parent()) {
parent()->remove(*this);
}
}
bool operator<(const Node &rhs) const {
return _name < rhs._name;
}
Directory *parent() const {
return _parent;
}
void setParent(Directory *parent) {
_parent = parent;
}
const std::string &name() const {
return _name;
}
bool isRoot() const {
return _isRoot;
}
std::string pathname() const {
std::ostringstream path;
if (parent()) {
path << parent()->pathname() << '/';
} else {
path << '/';
}
path << name();
return path.str();
}
private:
// Not defined:
Node(const Node &rhs);
Node &operator=(const Node &rhs);
private:
std::string _name;
Directory *_parent;
const bool _isRoot;
};
I think that because
Noderequires a reference toDirectoryduring construction, making a dummy node to search your set by name will make theNodeclass more cluttered.To use
setyou’d probably need to make a staticDirectorysomewhere, and use that as a dummy reference in a new dummy constructorNode(const std::string&). If you don’t declare thatexplicityou can use astringdirectly in your call toset::find.You could instead convert the class to use pointers… But that would change its internal semantics:
Directory&is always valid, whereasDirectory*doesn’t have to be. Ask yourself whether you want to make the semantics less clear to the reader simply because of your preference for thesetcontainer.So my opinion is pretty clear in this case… You have a choice: Either use
mapand keep your class clean, or usesetand write a bit of supporting junk code that has no use for anything else. =)