I wonder how is the most convenient way to have a sorted set, a set of pointers to objects, like
std::set<myClass*> mySet;
I want this set to be sorted by myClass::someProperty (say, an int).
Should I overload operator < in myClass? I'm not sure if it will work, because it's not a set of myClass, but a set of pointers.
How can I define a compare function?
Thank you very much.
You need to define a type (or a function) that dereferences the pointers and compares the attributes of the objects they point at, something on this general order:
class myClass {
int value;
public:
myClass(int i = 0) : value(i) {}
struct cmp {
bool operator()(myClass *const &a, myClass *const &b) const {
return a->value < b->value;
}
};
};
We they define the set something like this:
std::set<myClass*, myClass::cmp> mySet;
My advice, however, would be to store objects instead of pointers (if possible).
You can also specialize std::less for your myClass* as given below and then no need to pass comparator while creating set:
namespace std {
template<>
struct less<myClass*>
{
bool operator()(const myClass* k1, const myClass* k2) const
{
// Some code ...
}
};
}
I got a map like
typedef float(*function)(vector<int>);
map < string, function> mymap{
{ "maximum", &maxValue },
{ "minimum", &minValue },
{... , ...},
};
and got the definitions for functions that I used in the map as
float maxValue(vector<int> v){
auto cit = max_element(v.begin(), v.end());
return *cit;
}
and a similar function definitions for other functions that I used in the above map definition.
I got a class with two private data variables in a .h file as "string value;" and "float aggregate"
Now, my question is I want to define a constructor for this class and initialize the object's member data in.cpp file with values as
// aggregates is my class name
aggregates::aggregates(string val, const vector<int>& v): value(val), aggregate(.....)//here is my problem how can I initialize aggregate from the map that I mentioned above.
Thanks in advance
class aggregates
{
private:
float aggregate;
string value;
public:
aggregates(const string & val, const vector<int> & v, const map <string, function> & mymap) : value(val)
{
aggregate = mymap.at(value)(v); // instead of mymap[value](v), due to const&
}
};
When using a std::pair or std::map, we need to use "first" or "second" to access data. But the two variable name do not have clear meanings of what it really store for other co-workers that did not write this code. So if we can make aliases for "first" or "second", it would enhance much readability.
For example, the following code
static const std::map<std::string, std::pair<std::string, PFConvert>> COMM_MAP =
{ // keyword-> (caption, function)
{std::string("1"), {std::string("Big5 to Utf16LE"), &FileConvert_Big5ToUtf16LE}},
{std::string("2"), {std::string("Utf16LE to Utf8"), &FileConvert_Utf16LEToUtf8}},
{std::string("3"), {std::string("Utf8 to Big5"), &FileConvert_Utf8ToBig5}}
};
auto iterToExe = COMM_MAP.find(strTransType);
iterToExe->second.second();
The iterToExe->second.second(); has a truly bad readability.
So I try to use inherit to give aliases as following
template<typename PFComm>
class CCommContent : public std::pair<std::string, PFComm>
{
public:
std::string &strCaption = std::pair<std::string, PFComm>::first;
PFComm &pfComm = std::pair<std::string, PFComm>::second;
};
template<typename PFComm>
class CCommPair : public std::pair<std::string, CCommContent<PFComm>>
{
public:
std::string &strPattern = std::pair<std::string, CCommContent<PFComm>>::first;
CCommContent<PFComm> commContent = std::pair<std::string,CCommContent<PFComm>>::second;
};
template<typename PFComm>
class CCommMap : public std::map<std::string, CCommContent<PFComm>, std::less<std::string>, std::allocator<CCommPair<PFComm>>>
{};
But this comes to an another issue: I have to declare all the ctors, though i could call the base ctors, but it still not seems to be a smart method. I Just want to make aliases.
A simple way is to use macro ...... but it bypass the type checking. when using a nested structure, it may be a nightmare when debug.
Any advice or discussion would be appreciated.
Why not simply use your own struct with your own element names?
struct MyPair {
std::string strCaption;
PFComm pfComm;
};
With C++11 you can easily create new objects of it:
MyPair{std::string("Big5 to Utf16LE"), &FileConvert_Big5ToUtf16LE}}
And if you define your own operator<, you can have std::set work as a map:
bool operator<(const MyPair& a, const MyPair& b) {
return a.strCaption < b.strCaption;
}
typedef std::set<MyPair> MyPairMap;
Naturally, you can nest your custom structs to form more complex nested pairs, although in your case you might want to consider a flat triplet instead:
struct CommMapEntry {
std::string number;
std::string caption;
PFComm pfComm;
};
bool operator<(const MyPair& a, const MyPair& b) {
return a.number<b.number;
}
static const std::set<CommMapEntry> COMM_MAP;
How about some typedefs and accessor functions?
using CommEntry = std::pair<std::string, PFConvert>;
std::string const & getCaption(CommEntry const & e) { return e.first; }
PFConvert const & getFunction(CommEntry const & e) { return e.second; }
Now you can say:
auto it = COMM_MAP.find(strTransType);
if (it != COMM_MAP.end())
{
auto & c = getCaption(it->second);
auto & l = getLabel(it->second);
// ...
}
If you later change the details of the type, you just have adapt the accessor functions.
well, in c++11, we can using base::base in a derive class to use the base ctors. But note that vs2013 DO NOT compliant this. g++4.8 do.
I was wondering if this was possible, and if so how I'd go about doing so. If it's not possible I'll just have to add the elements during the constructor body.
Ideally I would like the map immutable after construction.
What I'm trying to achieve is adding two pairs to the map that are created from constructor parameters.
It's possible, by copy construction: invoke a function which build the map!
std::map<int,int> myFunc(int a, int b);
class MyObject
{
public:
MyObject(int a, int b): m_map(myFunc(a,b)) {}
private:
std::map<int,int> m_map;
};
Without extra tools you can only init std containers in initialization lists as far as their constructors support you. If you need more, map_list_of() et al from Boost.Assign do a great job.
Boost.Assign in action:
class C
{
const std::map<int,int> _vi;
public:
C() : _vi(boost::assign::map_list_of(1,1)(2,1)(3,2)) {}
}
edit: updated to std::map example.
There’s a map constructor that takes a range as a pair of iterators. Using that, you can construct something similar to your demands:
pair<int, int> init[] = { make_pair(1, 2), make_pair(2, 3) };
map<int, int> const mymap(init, init + 2);
Granted, not pretty.
The next version of C++ will come with better support for initialization lists. Until then, Boost.Assign is the next best thing.
I use an initializer class:
template<class K, class V>
class MapInitializer
{
std::map<K,V> m;
public:
operator std::map<K,V>() const
{
return m;
}
MapInitializer& Add( const K& k, const V& v )
{
m[ k ] = v;
return *this;
}
};
Then to put it to use:
class C
{
const std::map<int,const char*> m_map;
public:
C() : m_map( MapInitializer<int,const char*>()
.Add( 1, "Hi there" )
.Add( 2, "In the middle" )
.Add( 9, "Goodbye" ) )
{}
};
This is free (in the sense that you aren't building a map, copying it, and throwing the first away) because of C++'s return value optimization. The same thing can be done for initializing a vector or other standard containers.
Hope that helps!
What is the right way of initializing a static map? Do we need a static function that will initialize it?
Using C++11:
#include <map>
using namespace std;
map<int, char> m = {{1, 'a'}, {3, 'b'}, {5, 'c'}, {7, 'd'}};
Using Boost.Assign:
#include <map>
#include "boost/assign.hpp"
using namespace std;
using namespace boost::assign;
map<int, char> m = map_list_of (1, 'a') (3, 'b') (5, 'c') (7, 'd');
Best way is to use a function:
#include <map>
using namespace std;
map<int,int> create_map()
{
map<int,int> m;
m[1] = 2;
m[3] = 4;
m[5] = 6;
return m;
}
map<int,int> m = create_map();
It's not a complicated issue to make something similar to boost. Here's a class with just three functions, including the constructor, to replicate what boost did (almost).
template <typename T, typename U>
class create_map
{
private:
std::map<T, U> m_map;
public:
create_map(const T& key, const U& val)
{
m_map[key] = val;
}
create_map<T, U>& operator()(const T& key, const U& val)
{
m_map[key] = val;
return *this;
}
operator std::map<T, U>()
{
return m_map;
}
};
Usage:
std::map mymap = create_map<int, int >(1,2)(3,4)(5,6);
The above code works best for initialization of global variables or static members of a class which needs to be initialized and you have no idea when it gets used first but you want to assure that the values are available in it.
If say, you've got to insert elements into an existing std::map... here's another class for you.
template <typename MapType>
class map_add_values {
private:
MapType mMap;
public:
typedef typename MapType::key_type KeyType;
typedef typename MapType::mapped_type MappedType;
map_add_values(const KeyType& key, const MappedType& val)
{
mMap[key] = val;
}
map_add_values& operator()(const KeyType& key, const MappedType& val) {
mMap[key] = val;
return *this;
}
void to (MapType& map) {
map.insert(mMap.begin(), mMap.end());
}
};
Usage:
typedef std::map<int, int> Int2IntMap;
Int2IntMap testMap;
map_add_values<Int2IntMap>(1,2)(3,4)(5,6).to(testMap);
See it in action with GCC 4.7.2 here: http://ideone.com/3uYJiH
############### EVERYTHING BELOW THIS IS OBSOLETE #################
EDIT: The map_add_values class below, which was the original solution I had suggested, would fail when it comes to GCC 4.5+. Please look at the code above for how to add values to existing map.
template<typename T, typename U>
class map_add_values
{
private:
std::map<T,U>& m_map;
public:
map_add_values(std::map<T, U>& _map):m_map(_map){}
map_add_values& operator()(const T& _key, const U& _val)
{
m_map[key] = val;
return *this;
}
};
Usage:
std::map<int, int> my_map;
// Later somewhere along the code
map_add_values<int,int>(my_map)(1,2)(3,4)(5,6);
NOTE: Previously I used a operator [] for adding the actual values. This is not possible as commented by dalle.
##################### END OF OBSOLETE SECTION #####################
Here is another way that uses the 2-element data constructor. No functions are needed to initialize it. There is no 3rd party code (Boost), no static functions or objects, no tricks, just simple C++:
#include <map>
#include <string>
typedef std::map<std::string, int> MyMap;
const MyMap::value_type rawData[] = {
MyMap::value_type("hello", 42),
MyMap::value_type("world", 88),
};
const int numElems = sizeof rawData / sizeof rawData[0];
MyMap myMap(rawData, rawData + numElems);
Since I wrote this answer C++11 is out. You can now directly initialize STL containers using the new initializer list feature:
const MyMap myMap = { {"hello", 42}, {"world", 88} };
For example:
const std::map<LogLevel, const char*> g_log_levels_dsc =
{
{ LogLevel::Disabled, "[---]" },
{ LogLevel::Info, "[inf]" },
{ LogLevel::Warning, "[wrn]" },
{ LogLevel::Error, "[err]" },
{ LogLevel::Debug, "[dbg]" }
};
If map is a data member of a class, you can initialize it directly in header by the following way (since C++17):
// Example
template<>
class StringConverter<CacheMode> final
{
public:
static auto convert(CacheMode mode) -> const std::string&
{
// validate...
return s_modes.at(mode);
}
private:
static inline const std::map<CacheMode, std::string> s_modes =
{
{ CacheMode::All, "All" },
{ CacheMode::Selective, "Selective" },
{ CacheMode::None, "None" }
// etc
};
};
I would wrap the map inside a static object, and put the map initialisation code in the constructor of this object, this way you are sure the map is created before the initialisation code is executed.
Just wanted to share a pure C++ 98 work around:
#include <map>
std::map<std::string, std::string> aka;
struct akaInit
{
akaInit()
{
aka[ "George" ] = "John";
aka[ "Joe" ] = "Al";
aka[ "Phil" ] = "Sue";
aka[ "Smitty" ] = "Yando";
}
} AkaInit;
You can try:
std::map <int, int> mymap =
{
std::pair <int, int> (1, 1),
std::pair <int, int> (2, 2),
std::pair <int, int> (2, 2)
};
If you are stuck with C++98 and don't want to use boost, here there is the solution I use when I need to initialize a static map:
typedef std::pair< int, char > elemPair_t;
elemPair_t elemPairs[] =
{
elemPair_t( 1, 'a'),
elemPair_t( 3, 'b' ),
elemPair_t( 5, 'c' ),
elemPair_t( 7, 'd' )
};
const std::map< int, char > myMap( &elemPairs[ 0 ], &elemPairs[ sizeof( elemPairs ) / sizeof( elemPairs[ 0 ] ) ] );
This is similar to PierreBdR, without copying the map.
#include <map>
using namespace std;
bool create_map(map<int,int> &m)
{
m[1] = 2;
m[3] = 4;
m[5] = 6;
return true;
}
static map<int,int> m;
static bool _dummy = create_map (m);
In addition to the good top answer of using
const std::map<int, int> m = {{1,1},{4,2},{9,3},{16,4},{32,9}}
there's an additional possibility by directly calling a lambda that can be useful in a few cases:
const std::map<int, int> m = []()->auto {
std::map<int, int> m;
m[1]=1;
m[4]=2;
m[9]=3;
m[16]=4;
m[32]=9;
return m;
}();
Clearly a simple initializer list is better when writing this from scratch with literal values, but it does open up additional possibilities:
const std::map<int, int> m = []()->auto {
std::map<int, int> m;
for(int i=1;i<5;++i) m[i*i]=i;
m[32]=9;
return m;
}();
(Obviously it should be a normal function if you want to re-use it; and this does require recent C++.)
You have some very good answers here, but I'm to me, it looks like a case of "when all you know is a hammer"...
The simplest answer of to why there is no standard way to initialise a static map, is there is no good reason to ever use a static map...
A map is a structure designed for fast lookup, of an unknown set of elements. If you know the elements before hand, simply use a C-array. Enter the values in a sorted manner, or run sort on them, if you can't do this. You can then get log(n) performance by using the stl::functions to loop-up entries, lower_bound/upper_bound. When I have tested this previously they normally perform at least 4 times faster than a map.
The advantages are many fold...
- faster performance (*4, I've measured on many CPU's types, it's always around 4)
- simpler debugging. It's just easier to see what's going on with a linear layout.
- Trivial implementations of copy operations, should that become necessary.
- It allocates no memory at run time, so will never throw an exception.
- It's a standard interface, and so is very easy to share across, DLL's, or languages, etc.
I could go on, but if you want more, why not look at Stroustrup's many blogs on the subject.