I'm trying to sort sets by value rather than keys. I keep running into the illegal indirection issue. The error occurs everytime I try to insert through the LRU cache. I assumed the issue had to do with not using pointers so I converted a and b to pointers instead but it didn't work.
struct comp {
bool operator ()(const pair<int, int>& a, const pair<int, int>& b) {
return a.second < b.second;
}
};
typedef set<pair<int, int>, comp> S;
struct LRUCache {
S cache;
typedef S::iterator it;
int cap;
LRUCache(int capacity) {
cap = capacity;
}
int get(int key) {
}
void setC(int key, int value) {
cache.insert(key, value);
}
void initialize() {
vector<int> temp{ 5,20,35,20,83,17,5,1,0,239,242,42 };
for (int i = 0; i < temp.size(); i++) {
setC(i, temp[i]);
}
}
};
int main()
{
LRUCache test(5);
test.initialize();
test.setC(1, 5);
test.setC(12, 20);
return 0;
}
The issue is that your std::set consists of std::pair<int, int>, and you're not inserting that type into your set with this line:
cache.insert(key, value);
This calls the set::insert function that takes two parameters, and the compiler is trying to match the two int values you're passing with one of the overloads here that takes two parameters.
Instead what you want to do is call set::insert with a single parameter, namely the std::pair<int, int>.
Change the above line to this:
cache.insert({key, value});
or
cache.insert(std::make_pair(key, value));
Live Example
You could also have seen the issue more clearly if you used using or typedef for the std::pair.
typedef std::pair<int, int> IntPair;
typedef std::set<IntPair, comp> PairSet;
//...
PairSet cache;
//...
cache.insert(IntPair(key, value));
You know that you want to insert IntPair's into the set, thus the code above would have compiled with no issue.
Related
I was trying to make a map sort by value using a custom comparator but I couldn't figure out why I kept getting the error of "no matching call to compareByVal"
Here's what I had in my main.cpp:
#include <map>
#include <iostream>
struct compareByVal {
bool operator[](const std::pair<int,int> & a, const std::pair<int,int> & b)
return a.second < b.second;
}
int main() {
std::map<int,int,compareByVal> hash;
hash[1] = 5;
hash[2] = 2;
hash[3] = 10;
std::cout << hash.begin()->first << std::endl;
}
The first, simple problem is
struct compareByVal {
bool operator[](const std::pair<int,int> & a, const std::pair<int,int> & b)
return a.second < b.second;
}
should be
struct compareByVal {
bool operator()(const std::pair<int,int> & a, const std::pair<int,int> & b) const {
return a.second < b.second;
}
};
The second, serious problem is the signature of the compare is wrong. It should be
struct compareByVal {
bool operator()(const int leftKey, const int rightKey) const;
}
You can't access the value in the compare function. There is no (simple) way to sort a map by value.
Simply put, you cannot. Not sure which compiler you're using, but clang and gcc both give useful messages. with context.
clang:
static_assert(__is_invocable<_Compare&, const _Key&, const _Key&>{},
gcc:
if (__i == end() || key_comp()(__k, (*__i).first))
You can see that clang and gcc are both calling the compare method with only they key, and not a value. This is simply how maps work.
If you want to sort by value, you would have to create your own custom map, or, more realistically, use the value as the key instead. Creating your own map to achieve this would be more difficult than you'd think, since it would have to sort after any value is modified.
If you want to sort a std::map by its value, then you are using the wrong container. std::map is sorted by the keys by definition.
You can wrap key and value:
struct foo {
int key;
int value;
};
and then use a std::set<foo> that uses a comparator that only compares foo::value.
Well, first, the reason you're getting the error: "no matching call to compareByVal" is because map's comparator works only with the keys. So the comparator should like:
struct compareByVal {
template <typename T>
bool operator()(const T& a, const T& b) const
return a < b;
}
Coming on to what you want to achieve, I see two ways of doing so:
Copy all the elements of the map to a std::vector and sort that:
std::vector<std::pair<int,int> > v(hash.begin(), hash.end());
std::sort(v.begin(), v.end(), [](const auto& a, const auto& b) { return a.second < b.second; });
Copy all the elements of the map to another map with keys as values and values as keys. If values of your map are not unique, you can use a std::multimap instead.
This may be an X-Y issue.
If you need to sort by both key and value, then a single std::map may not be the most efficient choice.
In database theory, all the data would be placed into a single table. An index table would be created describing the access or sorting method. Data that needs to be sorted in more than one method would have multiple index tables.
In C++, the core table would be a std::vector. The indices would be std::map<key1, vector_index>, std::map<key2, vector_index>, where vector_index is the index of the item in the core table.
Example:
struct Record
{
int age;
std::string name;
};
// Core table
std::vector<Record> database;
// Index by age
std::map<int, unsigned int> age_index_table;
// Index by name
std::map<std::string, unsigned int> name_index_table;
// Fetching by age:
unsigned int database_index = age_index_table[42];
Record r = database[database_index];
// Fetching by name:
unsigned int database_index = name_index_table["Harry Potter"];
Record r = database[database_index];
You can learn more by searching the internet for "database index tables c++".
If it looks like a database and smells like a database ...
Finding a shortest path in a grid and struggling to set up priority queue properly.
struct position{
int row;
int col;
position* parent;
position(int a, int b):row(a),col(b), parent(nullptr){}
};
vector<position>vec;
priority_queue<pair<int, position>, vector<pair<int, position>>, greater<pair<int, position>>>pq;
int distance = 0;
position *t = new p(0,0);
pq.push(make_pair(distance, t));
Getting this error:
no matching function for call to ‘std::priority_queue, std::vector >, std::greater > >::push(std::pair)’
pq.push(make_pair(distance, t));
You need to write a functor ( or use a lambda) to compare the distance - position pair, std::greater won't automatically do it for you. Try this snippet:
struct position {
int row;
int col;
position* parent;
position(int a, int b) :row(a), col(b), parent(nullptr) {}
};
typedef std::pair<int, position> dist_pos_t;
class compare
{
public:
bool operator()(const dist_pos_t& lhs, const dist_pos_t& rhs)
{
return rhs.first < lhs.first;
}
};
std::priority_queue<dist_pos_t, std::vector<dist_pos_t>, compare >pq;
int main() {
int distance = 0;
position *t = new position(0, 0);
pq.push(std::make_pair(distance, *t));
}
Here Declaration of Priority Queue does not match with what you are trying to push.
Declaration should be something like
priority_queue<pair<obj1,obj2>pq;
obj1/obj2 can be anything like int or pair<obj,obj>
After such declaration you can use
pq.push(make_pair(obj1,obj2))
There are two major issues in your code.
First, your priority_queue is of std::pair<int, position>, but you are trying to push in a std::pair<int, position*>.
Second, std::greater<T> depends on the > operator of the underlying type T. In your case, it's std::pair<int, position>, whose > operator depends on the < operator of position (See this reference). You need to provide < for position, or, you can use a custom compare functor type.
I am trying to create a graph where each node is a pair of two int values. For this I have created a list adj of type pii (pair<int, int>). Now when I try to push_back a pii type node in the list it says
error: no match for 'operator[]' (operand types are 'std::list<std::pair<int, int> >*' and 'std::pair<int, int>')|
Here's my code. ( I haven't added the edges from main() yet). I've included all the necessary headers. I've searched but can't find a similar error.
#define pii pair<int, int>
#define lli long long int
using namespace std;
class graph
{
lli v;
list<pii> *adj;
public:
graph(lli v);
void addEdge(pii n, pii m);
void bfs(pii s);
};
graph::graph(lli v)
{
this->v = v;
adj = new list<pii >[v];
}
void graph::addEdge(pii n, pii m)
{
adj[n].push_back(m); //Error Line
}
void graph::bfs(pii s)
{
bool visited[v];
memset(visited, false, sizeof(visited));
list<pii> q;
list<pii>::iterator it;
q.push_back(s);
while(!q.empty())
{
pii temp = q.front();
q.pop_front();
for(it = adj[temp].begin() ; it != adj[temp].end() ; it++)
{
if(!visited[*it])
{
visited[*it] = true;
q.push_back(*it);
}
}
}
}
int main()
{
int n, m, i, j;
pii coordinates;
cin>>n>>m;
char input[n][m];
for(i = 0 ; i < n ; i++)
{
for(j = 0 ; j < m ; j++)
{
cin>>input[i];
make_pair(i, j);
}
}
graph(n*m);
return 0;
}
adj is of type std::list<std::pair<int, int>>*, which is a pointer. Pointers implement the following operator[]
T & operator[](T *, std::ptrdiff_t);
You're trying to call the following:
T& operator(T*, std::pair<int, int>);
You need to call operator[] with a type of std::ptrdiff_t which is a signed integer.
void graph::addEdge(***int*** n, pii m)
{
adj[n].push_back(m); //Error Line
}
As others have said, the direct answer to your question is that you are attempting to index a pointer with a pii (which you have #defined as pair<int,int>), and since a pii has no automatic conversion to an integer, it cannot be used as an index.
Looking at the broader picture, because of your abbreviated variable and method names, it is hard to see what it is you are trying to do. Is your intent that adj be an array of lists? If so, you just need to change the first parameter to addEdge to int, so that it can be used as an index into this array. But from a quick look through the method bfs, it looks like perhaps adj is intended to just be a list, in which case you can remove the asterisk from its declaration and eliminate its assignment in the graph constructor, and eliminate the indexing altogether. Or if what you are trying to do is to map one pii to another pii, you would need to use something like std::map instead of std::list.
I'm Javaer for many years and a newbie in C++. Recently I need to work on a C++ project, but experience some unpleasant issues when using C++, one of them is the std:map.
I'm trying to insert a key-value pair into a map function.
map[key]=value or map.emplace(key,value) works fine, but map.insert gives me [compilation error](), which I'm totally lost. Can someone helps?
class mystructure{
private:
int value;
public:
mystructure(int v){
value=v;
}
void print(){
std::cout<<value<<std::endl;
}
std::string get_Value(){
return std::to_string(value);
}
};
int main(void) {
std::map<std::string, mystructure> mymap;
std::vector<mystructure> v = std::vector<mystructure>();
v.push_back(*(new mystructure(17)));
v.push_back(*(new mystructure(12)));
v.push_back(*(new mystructure(23)));
for (int i=0;i<v.size();i++) {
mystructure temp=v.at(i);
mymap.insert(temp.get_Value(),temp);//compilation error
}
return 0;
}
Because std::map::insert accepts std::map::value_type (i.e. std::pair<const Key, T>) as its parameter.
You could use:
mymap.insert(std::pair<const std::string, mystructure>(temp.get_Value(), temp));
or
mymap.insert(std::make_pair(temp.get_Value(), temp));
or
mymap.insert({temp.get_Value(), temp});
LIVE
Consider the following:
struct A
{
int i;
double d;
std::string s;
};
std::list<A> list_A;
I'd like to copy all the elements of list_A to a map such that every pair in the map will consist of an element from list_A as value and its string s as key. Is there a way of doing it that is more elegant than looping through the list and insert each element along with its string as key to the map?
I love standard library algorithms and lambdas but it doesn't get much simpler than:
for (const A& value : list_A) {
map_A.insert(std::make_pair(value.s, value));
}
The other methods are doing the equivalent of this code and this loop is readable and just as fast.
This should get you the idea of how to use transform:
std::pair<std::string, A> pairify(const A& a) { return std::make_pair(a.s, a); }
std::transform(list.begin(), list.end(), std::inserter(map, map.end()), pairify);
The reason to use the inserter is:
An insert interator is a special type of output iterator designed to allow algorithms that usually overwrite elements (such as copy) to instead insert new elements automatically at a specific position in the container.
Sorry answered too quickly last time without details, here is a compilable code.
struct A
{
int i;
double d;
std::string s;
};
std::list<A> list_A;
std::pair<std::string, A> convert(const A &x) {
return make_pair(x.s,x);
}
int main() {
std::map<std::string,A> out;
std::transform(list_A.begin(), list_A.end(), std::inserter(out,out.end()),convert);
}
I may store it in a set: in this way there would not be data duplication in the map (s itself):
struct A
{
bool operator < (const A& r_) const { return (s < r_.s); }
int i;
double d;
std::string s;
};
std::list<A> list_A;
std::set<A> set_A;
for ( std::list<A>::const_iterator itr = list_A.begin(); itr != list_A.end(); ++itr ) {
if ( ! set_A.insert(*itr).second ) {
// Handle duplicated elements
}
}
I may keep the loop: in this way you could handle duplicated elements correctly.
If you use C++11 you can use lambda function with capture:
std::map<std::string, A> m;
std::list<A> l;
std::for_each(l.begin(), l.end(),
[&](const A& a) {
m.insert(std::make_pair(a.s, a));
});