Use vectors as key in unordered_map - c++

So I want to use vectors as keys in my unordered_map. I have followed this answer and I have the following
#include <iostream>
#include <cstdlib>
#include <tuple>
#include <unordered_map>
#include <vector>
#include <boost/functional/hash.hpp> /* hash_combine */
template <typename T>
struct vectorHasher{
std::size_t operator()(std::vector<T> &in) const
{
using boost::hash_value;
using boost::hash_combine;
// Start with a hash value of 0
std::size_t seed = 0;
T value;
for (int i=0; i< in.size(); i++)
{
value = static_cast<T>(in[i]);
hash_combine(seed, hash_value(value));
}
return seed;
}
};
int main()
{
typedef std::unordered_map< std::vector<std::size_t>, int, vectorHasher<std::vector<std::size_t> > > map_type;
map_type mymap;
std::vector<size_t> vec (3,100);
mymap[vec] = 1;
return 0;
}
, but I get the following error compiling the code.
In file included from mytest_vectorhasher.cpp:6:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/unordered_map:404:17: error:
no matching function for call to object of type 'const
vectorHasher<std::__1::vector<unsigned long, std::__1::allocator<unsigned long> >
>'
{return static_cast<const _Hash&>(*this)(__x);}
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/__hash_table:1976:21: note:
in instantiation of member function
'std::__1::__unordered_map_hasher<std::__1::vector<unsigned long,
std::__1::allocator<unsigned long> >,
std::__1::__hash_value_type<std::__1::vector<unsigned long,
std::__1::allocator<unsigned long> >, int>, vectorHasher<std::__1::vector<unsigned
long, std::__1::allocator<unsigned long> > >, true>::operator()' requested here
size_t __hash = hash_function()(__k);
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/unordered_map:1443:21: note:
in instantiation of function template specialization
'std::__1::__hash_table<std::__1::__hash_value_type<std::__1::vector<unsigned long,
std::__1::allocator<unsigned long> >, int>,
std::__1::__unordered_map_hasher<std::__1::vector<unsigned long,
std::__1::allocator<unsigned long> >,
std::__1::__hash_value_type<std::__1::vector<unsigned long,
std::__1::allocator<unsigned long> >, int>, vectorHasher<std::__1::vector<unsigned
long, std::__1::allocator<unsigned long> > >, true>,
std::__1::__unordered_map_equal<std::__1::vector<unsigned long,
std::__1::allocator<unsigned long> >,
std::__1::__hash_value_type<std::__1::vector<unsigned long,
std::__1::allocator<unsigned long> >, int>,
std::__1::equal_to<std::__1::vector<unsigned long, std::__1::allocator<unsigned
long> > >, true>,
std::__1::allocator<std::__1::__hash_value_type<std::__1::vector<unsigned long,
std::__1::allocator<unsigned long> >, int> >
>::__emplace_unique_key_args<std::__1::vector<unsigned long,
std::__1::allocator<unsigned long> >, const std::__1::piecewise_construct_t &,
std::__1::tuple<const std::__1::vector<unsigned long, std::__1::allocator<unsigned
long> > &>, std::__1::tuple<> >' requested here
return __table_.__emplace_unique_key_args(__k,
^
mytest_vectorhasher.cpp:41:10: note: in instantiation of member function
'std::__1::unordered_map<std::__1::vector<unsigned long,
std::__1::allocator<unsigned long> >, int, vectorHasher<std::__1::vector<unsigned
long, std::__1::allocator<unsigned long> > >,
std::__1::equal_to<std::__1::vector<unsigned long, std::__1::allocator<unsigned
long> > >, std::__1::allocator<std::__1::pair<const std::__1::vector<unsigned long,
std::__1::allocator<unsigned long> >, int> > >::operator[]' requested here
mymap[vec] = 1;
^
mytest_vectorhasher.cpp:13:17: note: candidate function not viable: no known conversion
from 'const std::__1::vector<unsigned long, std::__1::allocator<unsigned long> >'
to 'std::vector<vector<unsigned long, allocator<unsigned long> > > &' for 1st
argument
std::size_t operator()(std::vector<T> &in) const
^
1 error generated.
Is it something easy that I am messing up? What am I doing wrong?

There are two errors in this example. First, your hasher's operator() parameter should be const.
std::size_t operator()(const std::vector<T> &in) const
// Add const here ^^^^^
Second is you are defining your hashing type as vectorHasher<std::vector<std::size_t> > which would imply that T = std::vector<std::size_t>, meaning that you are trying to instantiate a hasher with std::size_t operator()(const std::vector<std::vector<std::size_t>> &in) const which is too many vectors. Remove the std::vector from your map type declaration like this :
typedef std::unordered_map< std::vector<std::size_t>, int, vectorHasher< std::size_t > > map_type;
// Remove std::vector from this template argument ^^^^^^^^^^^

The problem is you have the wrong parameter for operator(). When you construct the vectorHasher you use
vectorHasher<std::vector<std::size_t>>
Which means T is a
std::vector<std::size_t>
That means your operator() looks like
std::size_t operator()(std::vector<std::vector<std::size_t>> &in) const
Which is not what you want. What you can do is to use T directly like
std::size_t operator()(const T& in) const
Or change the vectorHasher to be
vectorHasher<std::size_t>

There are 2 errors in your code.
1) Since you defined your vectorHasher as
template <typename T>
struct vectorHasher{
std::size_t operator()(std::vector<T> &in) const
{
// ...
}
};
The following template instantiation vectorHasher<std::vector<std::size_t>> expands to:
struct vectorHasher{
std::size_t operator()(std::vector<std::vector<std::size_t>> &in) const
{
// ...
}
};
And, sure enough, std::vector<std::size_t> can't be passed to this function.
Change the instantiation of the template to vectorHasher<std::size_t>.
2) operator() of the struct used for hashing needs to take const value/reference, so you should change the signature of it to:
std::size_t operator()(std::vector<T> const &in) const
Live demo.

Related

C++ priority queue with find function on derived data type

Following this question: https://stackoverflow.com/a/16749994/1703586 I am have a derived class from std::priority_queue. What I really need is a priority queue with a find() functionality.
When I try to use my_priority_queue I get a whole bunch of error which I do not understand (pasted below). With std::priority_queue, it compiles fine.
Please help me understand the compiler error and solve the issue.
UPDATE:
Thanks to #rafix07, the addition of the constructor solved the compilation issue (for now).
However since my value is a tuple type, I wrote a custom find. See my find function. Due to which I still get other compilation issues.
Here is my standalone code:
#include <functional>
#include <queue>
#include <vector>
#include <iostream>
template<typename T>
void print_queue(T q) { // NB: pass by value so the print uses a copy
while(!q.empty()) {
auto x = q.top();
auto cost = std::get<0>( x );
auto s = std::get<2>( x );
std::cout << "{ cost=" << cost << " | " << std::get<0>(s) << "," << std::get<1>(s) << "," << std::get<2>(s) << "}";
q.pop();
}
std::cout << '\n';
}
typedef int OcTreeKey ;
typedef bool NodeOccupancyStatus;
typedef std::tuple<OcTreeKey, int, NodeOccupancyStatus > value_tt; //< key, depth, occupancy-enum
typedef std::tuple< float , value_tt, value_tt > dijstas_tuple; // cost to goal, parent of current, current node
template<
class T,
class Container = std::vector<T>,
class Compare = std::less<typename Container::value_type>
> class my_priority_queue : public std::priority_queue<T, Container, Compare>
{
public:
typedef typename
std::priority_queue<
T,
Container,
Compare>::container_type::const_iterator const_iterator;
const_iterator find(const T&val) const
{
auto node0 = std::get<2>(val);
int node0_depth = std::get<2>(node0);
auto first = this->c.cbegin();
auto last = this->c.cend();
while (first!=last) {
// if (*first==val) return first;
auto node1 = std::get<2>(first);
int node1_depth = std::get<2>(node1);
if( node0_depth == node1_depth ) return first;
++first;
}
return last;
}
};
int main() {
// Using lambda to compare elements.
auto cmp = [](dijstas_tuple left, dijstas_tuple right) { return ( std::get<0>(left) > std::get<0>(right) ); };
// std::priority_queue<dijstas_tuple, std::vector<dijstas_tuple>, decltype(cmp)> q3(cmp);
my_priority_queue<dijstas_tuple, std::vector<dijstas_tuple>, decltype(cmp)> q3(cmp);
auto node_null = std::make_tuple( 2.0, -1, true );
auto node_tup0 = std::make_tuple( 2.0, 14, true );
auto node_tup1 = std::make_tuple( 12.0, 12, true );
auto node_tup2 = std::make_tuple( 22.0, 12, true );
auto node_tup3 = std::make_tuple( 51.0, 13, true );
q3.push( std::make_tuple(8.0, node_null, node_tup0 ) );
q3.push( std::make_tuple(3.0, node_tup2, node_tup1 ) );
q3.push( std::make_tuple(7.0, node_tup2, node_tup2 ) );
q3.push( std::make_tuple(10.0, node_tup1, node_tup3 ) );
q3.find( std::make_tuple(8.0, node_null, node_tup0 ) );
q3.pop();
print_queue(q3);
}
Here is my compiler error:
custom_priority_queue.cpp: In function ‘int main()’:
custom_priority_queue.cpp:62:87: error: no matching function for call to ‘my_priority_queue<std::tuple<float, std::tuple<int, int, bool>, std::tuple<int, int, bool> >, std::vector<std::tuple<float, std::tuple<int, int, bool>, std::tuple<int, int, bool> > >, main()::<lambda(dijstas_tuple, dijstas_tuple)> >::my_priority_queue(main()::<lambda(dijstas_tuple, dijstas_tuple)>&)’
eue<dijstas_tuple, std::vector<dijstas_tuple>, decltype(cmp)> q3(cmp);
^
custom_priority_queue.cpp:32:9: note: candidate: my_priority_queue<std::tuple<float, std::tuple<int, int, bool>, std::tuple<int, int, bool> >, std::vector<std::tuple<float, std::tuple<int, int, bool>, std::tuple<int, int, bool> > >, main()::<lambda(dijstas_tuple, dijstas_tuple)> >::my_priority_queue(const my_priority_queue<std::tuple<float, std::tuple<int, int, bool>, std::tuple<int, int, bool> >, std::vector<std::tuple<float, std::tuple<int, int, bool>, std::tuple<int, int, bool> > >, main()::<lambda(dijstas_tuple, dijstas_tuple)> >&)
> class my_priority_queue : public std::priority_queue<T, Container, Compare>
^~~~~~~~~~~~~~~~~
custom_priority_queue.cpp:32:9: note: no known conversion for argument 1 from ‘main()::<lambda(dijstas_tuple, dijstas_tuple)>’ to ‘const my_priority_queue<std::tuple<float, std::tuple<int, int, bool>, std::tuple<int, int, bool> >, std::vector<std::tuple<float, std::tuple<int, int, bool>, std::tuple<int, int, bool> > >, main()::<lambda(dijstas_tuple, dijstas_tuple)> >&’
custom_priority_queue.cpp:32:9: note: candidate: my_priority_queue<std::tuple<float, std::tuple<int, int, bool>, std::tuple<int, int, bool> >, std::vector<std::tuple<float, std::tuple<int, int, bool>, std::tuple<int, int, bool> > >, main()::<lambda(dijstas_tuple, dijstas_tuple)> >::my_priority_queue(my_priority_queue<std::tuple<float, std::tuple<int, int, bool>, std::tuple<int, int, bool> >, std::vector<std::tuple<float, std::tuple<int, int, bool>, std::tuple<int, int, bool> > >, main()::<lambda(dijstas_tuple, dijstas_tuple)> >&&)
custom_priority_queue.cpp:32:9: note: no known conversion for argument 1 from ‘main()::<lambda(dijstas_tuple, dijstas_tuple)>’ to ‘my_priority_queue<std::tuple<float, std::tuple<int, int, bool>, std::tuple<int, int, bool> >, std::vector<std::tuple<float, std::tuple<int, int, bool>, std::tuple<int, int, bool> > >, main()::<lambda(dijstas_tuple, dijstas_tuple)> >&&’
Compilation issue after find
You didn't provide the constructor for your class.
It could be:
my_priority_queue(const Compare& cmp) :
std::priority_queue<T, Container, Compare>(cmp) {}
or you could introduce priority queue constructor by using declaration:
using std::priority_queue<T, Container, Compare>::priority_queue;
Since C++20 (where lambdas are default constructible)
my_priority_queue<dijstas_tuple, std::vector<dijstas_tuple>, decltype(cmp)> q3;
is enough, it will call base class default ctor priority_queue() : priority_queue(Compare(), Container()) { } which with default constructible lambda works fine.

Compiler error on inserting into a map of maps in shared memory

I'm trying to create a mapped matrix (essentially a map<int, map<int, int>>) that is stored in shared memory. I'm using boost and following the example of this answer: https://stackoverflow.com/a/33913753/5760608
But, I can't get this to compile. I get a super long error that I'm failing to parse.
Compiled using the following command: clang -std=c++11 -o shmem_sparse_matrix shmem_sparse_matrix.cpp -lpthread -lrt -lstdc++ -lm
Also, if you know of a shared memory implementation of such a matrix that I can use out of the box, let me know - I couldn't find one.
#include <iostream>
#include <exception>
#include <string>
#include <boost/interprocess/containers/map.hpp>
#include <boost/container/scoped_allocator.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
using namespace boost::interprocess;
static const std::string SHMEM_NAME = "MySharedMemorySegment";
namespace Shared {
typedef managed_shared_memory Segment;
typedef Segment::segment_manager SegmentManager;
typedef int KeyType;
template <typename T>
using Alloc = allocator<T, SegmentManager>;
using Scoped = boost::container::scoped_allocator_adaptor<Alloc<int>>;
typedef map<int, int, Scoped> Row;
typedef map<int, Row, Scoped> Matrix;
}
class ShmemTest {
private:
Shared::Segment *segment;
Shared::Matrix *matrix;
size_t shmem_block = 10000;
struct shm_remove {
shm_remove() { shared_memory_object::remove(SHMEM_NAME.data()); }
~shm_remove() { shared_memory_object::remove(SHMEM_NAME.data()); }
} remover;
public:
ShmemTest() {
segment = new managed_shared_memory(create_only, SHMEM_NAME.data(), shmem_block);
}
void create_matrix() {
matrix = segment->construct<Shared::Matrix>("SparseMatrix")(segment->get_segment_manager());
}
void insert_element(int i, int j, int k) {
Shared::Row &row = (*matrix)[i];
row[j] = k;
}
};
int main() {
ShmemTest shmem_test;
shmem_test.create_matrix();
for (int i = 0; i < 10; ++i) {
for (int j = 0; j < 10; ++j) {
shmem_test.insert_element(i, j, i);
}
}
}
Compiler output TLDR (only showing errors):
clang -std=c++11 -o shmem_sparse_matrix shmem_sparse_matrix.cpp -lpthread -lrt -lstdc++ -lm
/usr/local/include/boost/container/detail/tree.hpp:464:14: error: type 'const key_compare' (aka 'const boost::container::scoped_allocator_adaptor<boost::interprocess::allocator<int, boost::interprocess::segment_manager<char,
boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family, boost::interprocess::offset_ptr<void, long, unsigned long, 0>, 0>, iset_index> > >') does not provide a call operator
{ return this->key_comp()(key1, this->key_from(nonkey2)); }
/usr/local/include/boost/container/detail/tree.hpp:468:14: error: type 'const key_compare' (aka 'const boost::container::scoped_allocator_adaptor<boost::interprocess::allocator<int, boost::interprocess::segment_manager<char,
boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family, boost::interprocess::offset_ptr<void, long, unsigned long, 0>, 0>, iset_index> > >') does not provide a call operator
{ return this->key_comp()(this->key_from(nonkey1), key2); }
/usr/local/include/boost/intrusive/detail/tree_value_compare.hpp:98:14: error: type 'const key_compare' (aka 'const boost::container::scoped_allocator_adaptor<boost::interprocess::allocator<int, boost::interprocess::segment_manager<char,
boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family, boost::interprocess::offset_ptr<void, long, unsigned long, 0>, 0>, iset_index> > >') does not provide a call operator
{ return this->key_comp()(KeyOfValue()(value1), KeyOfValue()(value2)); }
/usr/local/include/boost/container/scoped_allocator.hpp:443:4: error: constructor for 'boost::container::dtl::scoped_allocator_adaptor_base<boost::interprocess::allocator<int, boost::interprocess::segment_manager<char,
boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family, boost::interprocess::offset_ptr<void, long, unsigned long, 0>, 0>, iset_index> >>' must explicitly initialize the base class 'boost::interprocess::allocator<int,
boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family, boost::interprocess::offset_ptr<void, long, unsigned long, 0>, 0>, iset_index> >' which does not have a default constructor
scoped_allocator_adaptor_base()
shmem_sparse_matrix.cpp:47:37: note: in instantiation of member function 'boost::container::map<int, boost::container::map<int, int, boost::container::scoped_allocator_adaptor<boost::interprocess::allocator<int,
boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family, boost::interprocess::offset_ptr<void, long, unsigned long, 0>, 0>, iset_index> > >, void, void>,
boost::container::scoped_allocator_adaptor<boost::interprocess::allocator<int, boost::interprocess::segment_manager<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family, boost::interprocess::offset_ptr<void, long,
unsigned long, 0>, 0>, iset_index> > >, void, void>::operator[]' requested here
Shared::Row &row = (*matrix)[i];
/usr/local/include/boost/container/detail/tree.hpp:464:14: error: type 'const key_compare' (aka 'const boost::container::scoped_allocator_adaptor<boost::interprocess::allocator<int, boost::interprocess::segment_manager<char,
boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family, boost::interprocess::offset_ptr<void, long, unsigned long, 0>, 0>, iset_index> > >') does not provide a call operator
{ return this->key_comp()(key1, this->key_from(nonkey2)); }
/usr/local/include/boost/container/detail/tree.hpp:468:14: error: type 'const key_compare' (aka 'const boost::container::scoped_allocator_adaptor<boost::interprocess::allocator<int, boost::interprocess::segment_manager<char,
boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family, boost::interprocess::offset_ptr<void, long, unsigned long, 0>, 0>, iset_index> > >') does not provide a call operator
{ return this->key_comp()(this->key_from(nonkey1), key2); }
/usr/local/include/boost/intrusive/detail/tree_value_compare.hpp:98:14: error: type 'const key_compare' (aka 'const boost::container::scoped_allocator_adaptor<boost::interprocess::allocator<int, boost::interprocess::segment_manager<char,
boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family, boost::interprocess::offset_ptr<void, long, unsigned long, 0>, 0>, iset_index> > >') does not provide a call operator
{ return this->key_comp()(KeyOfValue()(value1), KeyOfValue()(value2)); }
shmem_sparse_matrix.cpp:48:12: note: in instantiation of member function 'boost::container::map<int, int, boost::container::scoped_allocator_adaptor<boost::interprocess::allocator<int, boost::interprocess::segment_manager<char,
boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family, boost::interprocess::offset_ptr<void, long, unsigned long, 0>, 0>, iset_index> > >, void, void>::operator[]' requested here
row[j] = k;
Full compiler output: https://gist.github.com/ffrankies/80114b648e9d88c2fd26e361c8b34111
You need to define the typedef's properly:
typedef managed_shared_memory Segment;
typedef Segment::segment_manager SegmentManager;
typedef int KeyType;
typedef map<int, int> MappedType;
typedef std::pair<const KeyType, MappedType> ValueType;
typedef allocator<ValueType, SegmentManager> ShmemAllocator;
typedef map<KeyType, MappedType, std::less<KeyType>, ShmemAllocator> Matrix;
Then one little change in insert_element() to use auto: auto &row = (*matrix)[i]; and that should work. Below full working example:
#include <iostream>
#include <exception>
#include <string>
#include <boost/interprocess/containers/map.hpp>
#include <boost/container/scoped_allocator.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
using namespace boost::interprocess;
static const std::string SHMEM_NAME = "MySharedMemorySegment";
namespace Shared {
typedef managed_shared_memory Segment;
typedef Segment::segment_manager SegmentManager;
typedef int KeyType;
typedef map<int, int> MappedType;
typedef std::pair<const KeyType, MappedType> ValueType;
typedef allocator<ValueType, SegmentManager> ShmemAllocator;
typedef map<KeyType, MappedType, std::less<KeyType>, ShmemAllocator> Matrix;
}
class ShmemTest {
private:
Shared::Segment *segment;
Shared::Matrix *matrix;
size_t shmem_block = 10000;
struct shm_remove {
shm_remove() { shared_memory_object::remove(SHMEM_NAME.data()); }
~shm_remove() { shared_memory_object::remove(SHMEM_NAME.data()); }
} remover;
public:
ShmemTest() {
segment = new managed_shared_memory(create_only, SHMEM_NAME.data(), shmem_block);
}
void create_matrix() {
matrix = segment->construct<Shared::Matrix>("SparseMatrix")(segment->get_segment_manager());
}
void insert_element(int i, int j, int k) {
auto &row = (*matrix)[i];
row[j] = k;
}
};
int main() {
ShmemTest shmem_test;
shmem_test.create_matrix();
for (int i = 0; i < 10; ++i) {
for (int j = 0; j < 10; ++j) {
shmem_test.insert_element(i, j, i);
}
}
}

fake iterator operations for enumerating all the node values in a graph

I'm trying to have a fake iterator for enumerating all the node values in a graph. The graph can be abstracted in any sequence, as long as the sequence consists of all and only the nodes in the graph. There should be a mutable qualifier, and the 4 operations' implementations are short and supposed to be only 1 line per function.
The test case for the 4 fake iterator operations is as follow, it is supposed to print out a string of values of the graph nodes through the fake iterator operations:
gdwg::Graph<std::string,int>> g
for (g.begin(); !g.end(); g.next())
std::cout << g.value() << std::end;
I declared the graph as the following:
template <typename N, typename E> class Graph {
private:
struct Node;
struct Edge;
struct Node {
N val_;
int numEdges_;
int numIncomingEdges_;
std::set<std::shared_ptr<Edge>> edges_;
std::set<std::shared_ptr<Edge>> incomingEdges_;
Node() {}
Node(const N x) : val_{x} { numEdges_=0; numIncomingEdges_=0; }
void printNode(N n);
~Node();
void update();
};
struct Edge {
std::weak_ptr<Node> orig;
std::weak_ptr<Node> dest;
E val_;
Edge(std::shared_ptr<Node> o, std::shared_ptr<Node> d, E x);
Edge() {};
void printEdge();
~Edge();
};
// fake iterator operations
void begin() const;
bool end() const;
void next() const;
const N& value() const;
public:
friend class Node_Iterator<N, E>;
friend class Edge_Iterator<N, E>;
private:
std::map< N, std::shared_ptr<Node> > nodes_;
// fake iterator to be used for the four operations begin(), end(), next() and value()
mutable typename std::map< N, std::shared_ptr<Node> >::iterator fakeIter_;
};
The implementation of the 4 operations are as follows:
// void begin() const: Sets an internal iterator, i.e., ``pointer'' to the first element of a sequence.
template <typename N, typename E>
void Graph<N,E>::begin() const {
// gets iterator to the first key/value pair in map sequence
fakeIter_ = nodes_.begin();
}
// bool end() const: Returns true if the iterator goes past the last element of the sequence and false otherwise.
template <typename N, typename E>
bool Graph<N,E>::end() const {
// return true if iterator goes past last element, otherwise return false
return ((fakeIter_ == nodes_.end()) ? true : false);
}
// void next() const: Moves the iterator to the next element of the sequence.
template <typename N, typename E>
void Graph<N,E>::next() const {
fakeIter_ = std::next(fakeIter_, 1);
}
// const N& value() const: Returns the value of the node pointed to by the iterator.
template <typename N, typename E>
const N& Graph<N,E>::value() const {
return fakeIter_->second->val_;
}
When I try to compile, there is a bunch of errors popping up. I'm just wondering whether I'm implementing the fake iterator operations correctly and if there is any way to improve it if need be?
tests/Graph.tem: In instantiation of ‘void gdwg::Graph<N, E>::begin() const [with N = unsigned int; E = int]’:
tests/test13.cpp:23:15: required from here
tests/Graph.tem:713:12: error: no match for ‘operator=’ (operand types are ‘std::map<unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node>, std::less<unsigned int>, std::allocator<std::pair<const unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node> > > >::iterator {aka std::_Rb_tree_iterator<std::pair<const unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node> > >}’ and ‘std::map<unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node>, std::less<unsigned int>, std::allocator<std::pair<const unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node> > > >::const_iterator {aka std::_Rb_tree_const_iterator<std::pair<const unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node> > >}’)
fakeIter_ = nodes_.begin();
In file included from /usr/local/include/c++/6.1.0/map:60:0,
from tests/Graph.h:19,
from tests/test13.cpp:3:
/usr/local/include/c++/6.1.0/bits/stl_tree.h:174:12: note: candidate: constexpr std::_Rb_tree_iterator<std::pair<const unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node> > >& std::_Rb_tree_iterator<std::pair<const unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node> > >::operator=(const std::_Rb_tree_iterator<std::pair<const unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node> > >&)
struct _Rb_tree_iterator
^~~~~~~~~~~~~~~~~
/usr/local/include/c++/6.1.0/bits/stl_tree.h:174:12: note: no known conversion for argument 1 from ‘std::map<unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node>, std::less<unsigned int>, std::allocator<std::pair<const unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node> > > >::const_iterator {aka std::_Rb_tree_const_iterator<std::pair<const unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node> > >}’ to ‘const std::_Rb_tree_iterator<std::pair<const unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node> > >&’
/usr/local/include/c++/6.1.0/bits/stl_tree.h:174:12: note: candidate: constexpr std::_Rb_tree_iterator<std::pair<const unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node> > >& std::_Rb_tree_iterator<std::pair<const unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node> > >::operator=(std::_Rb_tree_iterator<std::pair<const unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node> > >&&)
/usr/local/include/c++/6.1.0/bits/stl_tree.h:174:12: note: no known conversion for argument 1 from ‘std::map<unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node>, std::less<unsigned int>, std::allocator<std::pair<const unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node> > > >::const_iterator {aka std::_Rb_tree_const_iterator<std::pair<const unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node> > >}’ to ‘std::_Rb_tree_iterator<std::pair<const unsigned int, std::shared_ptr<gdwg::Graph<unsigned int, int>::Node> > >&&’
What that first error is saying is that, as your begin function is const, nodes_.begin() will return a const_iterator. This cannot be assigned to an iterator, the type of fakeIter_.
You'll need to remove the const from begin, change the type of fakeIter_, or change your implementation (begin should return an iterator, not void).

I'm trying to nest boost's "map_list_of" in C++03, but apparently construction is ambiguous?

Consider this:
#include <iostream>
#include <map>
#include <string>
#include <boost/assign/list_of.hpp>
using boost::assign::map_list_of;
const std::map<int, std::map<int, char> > test = map_list_of
(100, map_list_of
(1, 'a')
(2, 'b')
)
(101, map_list_of
(1, 'c')
(2, 'd')
)
;
int main()
{
std::cout << test.find(101)->second.find(2)->second << "\n";
}
I wanted the result to be a program that, when executed, outputs d.
Instead, I get this:
$ clang++ -std=c++03 -O2 -Wall -pedantic -pthread main.cpp
In file included from main.cpp:1:
In file included from /usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/iostream:39:
In file included from /usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/ostream:38:
In file included from /usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/ios:40:
In file included from /usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/bits/char_traits.h:39:
In file included from /usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/bits/stl_algobase.h:64:
/usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/bits/stl_pair.h:119:22: error: call to constructor of 'std::map<int, char, std::less<int>, std::allocator<std::pair<const int, char> > >' is ambiguous
: first(__p.first), second(__p.second) { }
^ ~~~~~~~~~~
/usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/bits/stl_tree.h:1843:29: note: in instantiation of function template specialization 'std::pair<const int, std::map<int, char, std::less<int>, std::allocator<std::pair<const int, char> > > >::pair<int, boost::assign_detail::generic_list<std::pair<int, char> > >' requested here
_M_insert_unique_(end(), *__first);
^
/usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/bits/stl_map.h:255:16: note: in instantiation of function template specialization 'std::_Rb_tree<int, std::pair<const int, std::map<int, char, std::less<int>, std::allocator<std::pair<const int, char> > > >, std::_Select1st<std::pair<const int, std::map<int, char, std::less<int>, std::allocator<std::pair<const int, char> > > > >, std::less<int>, std::allocator<std::pair<const int, std::map<int, char, std::less<int>, std::allocator<std::pair<const int, char> > > > > >::_M_insert_unique<std::_Deque_iterator<std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> > >, std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> > > &, std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> > > *> >' requested here
{ _M_t._M_insert_unique(__first, __last); }
^
/usr/local/include/boost/assign/list_of.hpp:163:20: note: in instantiation of function template specialization 'std::map<int, std::map<int, char, std::less<int>, std::allocator<std::pair<const int, char> > >, std::less<int>, std::allocator<std::pair<const int, std::map<int, char, std::less<int>, std::allocator<std::pair<const int, char> > > > > >::map<std::_Deque_iterator<std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> > >, std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> > > &, std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> > > *> >' requested here
return Container( begin(), end() );
^
/usr/local/include/boost/assign/list_of.hpp:142:20: note: in instantiation of function template specialization 'boost::assign_detail::converter<boost::assign_detail::generic_list<std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> > > >, std::_Deque_iterator<std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> > >, std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> > > &, std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> > > *> >::convert<std::map<int, std::map<int, char, std::less<int>, std::allocator<std::pair<const int, char> > >, std::less<int>, std::allocator<std::pair<const int, std::map<int, char, std::less<int>, std::allocator<std::pair<const int, char> > > > > > >' requested here
return convert<Container>( c, tag_type() );
^
/usr/local/include/boost/assign/list_of.hpp:436:49: note: in instantiation of function template specialization 'boost::assign_detail::converter<boost::assign_detail::generic_list<std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> > > >, std::_Deque_iterator<std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> > >, std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> > > &, std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> > > *> >::convert_to_container<std::map<int, std::map<int, char, std::less<int>, std::allocator<std::pair<const int, char> > >, std::less<int>, std::allocator<std::pair<const int, std::map<int, char, std::less<int>, std::allocator<std::pair<const int, char> > > > > > >' requested here
return this-> BOOST_NESTED_TEMPLATE convert_to_container<Container>();
^
main.cpp:7:50: note: in instantiation of function template specialization 'boost::assign_detail::generic_list<std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> > > >::operator map<std::map<int, std::map<int, char, std::less<int>, std::allocator<std::pair<const int, char> > >, std::less<int>, std::allocator<std::pair<const int, std::map<int, char, std::less<int>, std::allocator<std::pair<const int, char> > > > > > >' requested here
const std::map<int, std::map<int, char> > test = map_list_of
^
/usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/bits/stl_map.h:171:7: note: candidate constructor
map(const _Compare& __comp,
^
/usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/bits/stl_map.h:182:7: note: candidate constructor
map(const map& __x)
^
1 error generated.
(similar results under GCC)
How can I resolve this?
I get a similar error even if I use std::map<int, char>(map_list_of(...)) instead of map_list_of(...) for those inner maps.
C++03 defines two constructors for map that can be called with one argument [lib.map]p2:
explicit map(const Compare& comp = Compare(),
const Allocator& = Allocator());
// [...]
map(const map<Key,T,Compare,Allocator>& x);
boost's map_list_of creates an object of a generic_list class template instantiation, from the recent SVN:
template< class Key, class T >
inline assign_detail::generic_list< std::pair
<
BOOST_DEDUCED_TYPENAME assign_detail::assign_decay<Key>::type,
BOOST_DEDUCED_TYPENAME assign_detail::assign_decay<T>::type
> >
map_list_of( const Key& k, const T& t )
Where the primary generic_list template contains the following conversion operator:
template< class Container >
operator Container() const
{
return this-> BOOST_NESTED_TEMPLATE convert_to_container<Container>();
}
Both map constructors are viable, as this operator allows conversion to both map and Compare. As far as I know, you cannot SFINAE-constrain a conversion operator in C++03.
The map is constructed explicitly when inserting a new node in the outer map. A pair of iterators is used to iterate over the inner generic_list to construct the outer map. Dereferencing this iterator yields a std::pair<int, boost::assign_detail::generic_list<std::pair<int, char> >. The node (value) type of the outer map is std::pair<int const, std::map<int, char> >.
Therefore, the compiler tries to construct the latter type from the former. In C++03, this pair constructor is not SFINAE-constrained, since that's not possible in C++03. [lib.pairs]p1
template<class U, class V> pair(const pair<U, V> &p);
libstdc++ implements this as follows:
template<class _U1, class _U2>
pair(const pair<_U1, _U2>& __p)
: first(__p.first), second(__p.second) { }
I'm not entirely sure if that's compliant, since [lib.pairs]p4
Effects: Initializes members from the corresponding members of the argument, performing implicit conversions as needed.
(But, as I said, SFINAE on ctors cannot be implemented in C++03.)
In C++11 and 14, this also fails, but for a different reason. Here, the pair constructors are SFINAE-constrained. But the constrain requires implicit convertibility (is_convertible), while the program has UB if the target pair of types cannot be constructed from the sources (is_constructible). I've written a bit more about this issue in another SO answer. Interestingly, a proposed solution N4387 to the issue mentioned in that other question says:
It should be noted here, that for the general case the
std::is_constructible<T, U>::value requirement for the non-explicit
constructor which is constrained on std::is_convertible<U, T>::value
is not redundant, because it is possible to create types that can be
copy-initialized but not direct-initialized
This is exactly the case we run into here: A map can be copy-initialized from a generic_list, since this makes the explicit constructor non-viable. But a map cannot be direct-initialized from generic_list, since this makes the conversion ambiguous.
As far as I can see, N4387 does not solve the problem in the OP. On the other hand, with uniform initialization, we have an alternative to map_list_of. And we can SFINAE-constrain conversion operators since C++11.
One solution is to eliminate the explicit constructor by only allowing implicit conversions:
template<typename T> T implicit_cast(T t) { return t; }
implicit_cast<InnerMap>( map_list_of(1, 'a')(2, 'b') )
But there's a more direct way: simply use the convert_to_container member function of generic_list's base class converter (also a class template):
map_list_of(1, 'a')(2, 'b').convert_to_container<InnerMap>()

C++: Help Creating unordered_map with User-Defined Hash/Equality

I'm trying to create a std::unordered_map, using a user-defined hash function and equality predicate, for matrix rows of integral built-in types. I use std::bind, since I need the hashing and equality functors to work for variable ranges. How would I get the following code to compile and work as intended? I'm guessing that my mistake is towards the bottom of the listing, where I use std::bind and instantiate the std::unordered_map.
Some clarifications:
I can't use boost::hash_combine because I care about individual bits in the integers stored in the matrix row, so unless I create my own iterator, boost::hash_combine would combine whole integers, leading to erroneous results. Also, I need the hash and equality functors to work on ranges from 1 to ~200,000, so specifying the range in template parameters would not be a reasonable option.
template <class T,class F,class A>
struct row_hash_right : std::function<size_t(
ublas::matrix_row<ublas::matrix<T,F,A>>,
unsigned, unsigned)>
{
typedef ublas::matrix_row<ublas::matrix<T,F,A>> row_type;
// I want to use std::bind to bind the last two arguments.
size_t operator()(row_type& a, unsigned s, unsigned e)
{
// Implementation of hash function.
}
};
template <class T,class F,class A>
struct row_equal_right : std::function<bool(
ublas::matrix_row<ublas::matrix<T,F,A>>,
ublas::matrix_row<ublas::matrix<T,F,A>>,
unsigned, unsigned)>
{
typedef ublas::matrix_row<ublas::matrix<T,F,A>> row_type;
bool operator()(row_type& a, row_type& b, unsigned s, unsigned e)
{
// Implementation of equality predicate.
}
};
// Inside a function.
for (unsigned i = 0; i < len; ++i) {
for (unsigned j = i + 1; j < len; ++j) {
auto x = std::bind(r_hash, _1, i, j);
auto y = std::bind(r_equal, _1, _2, i, j);
// ERROR:
std::unordered_map<row_type, unsigned,
decltype(x), decltype(y)> m(256, x, y);
}
}
The error:
Here is (what I think) the most important part of the error produced upon attempted compilation:
/usr/include/c++/4.6/bits/stl_pair.h:92:11: error: ‘std::pair<_T1,
_T2>::first’ has incomplete type
/usr/include/boost/numeric/ublas/fwd.hpp:73:11: error: declaration of ‘const struct
boost::numeric::ublas::matrix_row,
boost::numeric::ublas::unbounded_array > > >’
If you want the see the whole thing, I've dumped it all here:
In file included from /usr/include/c++/4.6/bits/stl_algobase.h:65:0,
from /usr/include/c++/4.6/bits/char_traits.h:41,
from /usr/include/c++/4.6/ios:41,
from /usr/include/c++/4.6/ostream:40,
from /usr/include/c++/4.6/iostream:40,
from src/test/read_test.cpp:1:
/usr/include/c++/4.6/bits/stl_pair.h: In instantiation of ‘std::pair, boost::numeric::ublas::unbounded_array > > >, unsigned int>’:
/usr/include/c++/4.6/bits/stl_function.h:486:12: instantiated from ‘std::_Select1st, boost::numeric::ublas::unbounded_array > > >, unsigned int> >’
/usr/include/c++/4.6/bits/hashtable_policy.h:789:20: instantiated from ‘std::__detail::_Hash_code_base, boost::numeric::ublas::unbounded_array > > >, std::pair, boost::numeric::ublas::unbounded_array > > >, unsigned int>, std::_Select1st, boost::numeric::ublas::unbounded_array > > >, unsigned int> >, std::_Bind, boost::numeric::ublas::unbounded_array > >(std::_Placeholder, std::_Placeholder, unsigned int, unsigned int)>, std::_Bind, boost::numeric::ublas::unbounded_array > >(std::_Placeholder, unsigned int, unsigned int)>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, false>’
/usr/include/c++/4.6/bits/hashtable.h:105:11: instantiated from ‘std::_Hashtable, boost::numeric::ublas::unbounded_array > > >, std::pair, boost::numeric::ublas::unbounded_array > > >, unsigned int>, std::allocator, boost::numeric::ublas::unbounded_array > > >, unsigned int> >, std::_Select1st, boost::numeric::ublas::unbounded_array > > >, unsigned int> >, std::_Bind, boost::numeric::ublas::unbounded_array > >(std::_Placeholder, std::_Placeholder, unsigned int, unsigned int)>, std::_Bind, boost::numeric::ublas::unbounded_array > >(std::_Placeholder, unsigned int, unsigned int)>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, false, false, true>’
/usr/include/c++/4.6/bits/unordered_map.h:44:11: instantiated from ‘std::__unordered_map, boost::numeric::ublas::unbounded_array > > >, unsigned int, std::_Bind, boost::numeric::ublas::unbounded_array > >(std::_Placeholder, unsigned int, unsigned int)>, std::_Bind, boost::numeric::ublas::unbounded_array > >(std::_Placeholder, std::_Placeholder, unsigned int, unsigned int)>, std::allocator, boost::numeric::ublas::unbounded_array > > >, unsigned int> >, false>’
/usr/include/c++/4.6/bits/unordered_map.h:256:11: instantiated from ‘std::unordered_map, boost::numeric::ublas::unbounded_array > > >, unsigned int, std::_Bind, boost::numeric::ublas::unbounded_array > >(std::_Placeholder, unsigned int, unsigned int)>, std::_Bind, boost::numeric::ublas::unbounded_array > >(std::_Placeholder, std::_Placeholder, unsigned int, unsigned int)>, std::allocator, boost::numeric::ublas::unbounded_array > > >, unsigned int> > >’
./sal/alg/ehh.hpp:144:31: instantiated from ‘sal::ehh_results sal::compute_ehh(boost::numeric::ublas::matrix&, unsigned int) [with FloatType = double, T = unsigned int, F = boost::numeric::ublas::basic_row_major, A = boost::numeric::ublas::unbounded_array >]’
src/test/read_test.cpp:11:51: instantiated from here
/usr/include/c++/4.6/bits/stl_pair.h:92:11: error: ‘std::pair::first’ has incomplete type
/usr/include/boost/numeric/ublas/fwd.hpp:73:11: error: declaration of ‘const struct boost::numeric::ublas::matrix_row, boost::numeric::ublas::unbounded_array > > >’
If you want to hash a range of things, you need something like hash_combine(). I usually lift this function from Boost (surprisingly, it wasn't included in the standard!). Here's how I'd use it on std::arrays, and I trust you can manipulate this into something to work on matrix rows:
#include <array>
template <class T>
inline void hash_combine(std::size_t & seed, const T & v)
{
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
namespace std
{
template<typename T, size_t N> struct hash<array<T, N>>
{
inline size_t operator()(const array<T, N> & a) const
{
size_t seed = 0;
for (size_t i = 0; i != N; ++i)
{
::hash_combine(seed, a[i]);
}
return seed;
}
};
}
(The above specialisation allows you to use std::unordered_set<std::array<int, 10>> etc.)
If the matrix rows don't come with an equality predicate, you could also add a specialization of std::equal_to for matrix rows.