I can't pass (custom) priority_queue reference. Priority queue was customized using lambda. Is there any workaround? I tried using functors and all that, but none of them would let me go past line with the priority_queue creation at all without failing on the compilation step with various issues where priority_queue constructor wouldn't accept the sorting method. I guess it cannot reach the lambda or it just needs some special type declaration in the header, but I can't figure it out
Here is a simplified version of my code.
#include <queue>
#include <memory>
struct Node
{
int full_dist,
dist1,
dist2;
Node(int d1, int d2) { full_dist = d1 + d2; }
};
void some_processing(std::priority_queue<std::shared_ptr<Node>>& nodes)
{
//something being done with the queue
}
int main()
{
auto comp = [] (const std::shared_ptr<Node>& l, const std::shared_ptr<Node> r) -> bool
{ return l->full_dist > r->full_dist; };
std::priority_queue<std::shared_ptr<Node>, std::vector<std::shared_ptr<Node>>, decltype(comp)> nodes(comp);
some_processing(nodes);//breaks here
}
Here is the error I've got on this example:
test.cpp:24:24: error: invalid initialization of reference of type ‘std::priority_queue<std::shared_ptr<Node> >&’
from expression of type ‘std::priority_queue<std::shared_ptr<Node>, std::vector<std::shared_ptr<Node>, std::allocator<std::shared_ptr<Node> > >, main()::__lambda0>’
some_processing(nodes);
Make the function templated on the comparison type.
template<typename CompT>
void some_processing(
std::priority_queue<std::shared_ptr<Node>,
std::vector<std::shared_ptr<Node>>,
CompT> & nodes)
{
// something being done with the queue
}
Or keep things simple and just make the whole container type templated.
template<typename QueueT>
void some_processing(QueueT& nodes)
{
// something being done with the queue
}
Your priority queue is
std::priority_queue<std::shared_ptr<Node>,
std::vector<std::shared_ptr<Node>>, decltype(comp)>
That's it's declared type. The parameter to your function is a reference to:
std::priority_queue<std::shared_ptr<Node>>
This is a completely different type. You can't pass a reference to one type to a function that expects a reference to a completely different type as a parameter. Each instance of a template class is unique. The difference between the first and the second class, here, is the same difference as a difference between class A and class B.
Related
I am trying to define a class where the main element is a list of queues.
The problem is that each queue can be of different types.
Here is an example of what I would like to get. If we define these queues:
queue1 (type int) = {5,7,3,4,5}
queue2 (type string) = {"Sam","Peter"}
queue3 (type string) = {"January","February","March"}
queue4 (type int) = {10}
Then, I would like to be able to create objects like these:
object1.list_of_queues = {queue1,queue2}
object2.list_of_queues = {queue2,queue3,queue4}
All of them based on the same class.
I am able to create a type "list of queues", but all of them share the same type:
typedef std::queue<std::string> queue_type; //queue of strings
typedef std::list<queue_type> list_type; //list of queues of (only) strings
Is there any solution without using lists to avoid this type mixing? I was thinking about encapsulating the queues into a new defined type. Or creating a list of pointers, where every pointer points to the right queue, but I don't know if this is possible.
Thank you in advance!
Is it possible to define a list of queues where each queue can be of different types?
Not directly.
A std::queue of one element type is a different type from a queue of another element type. std::list is a homogeneous container: All elements have the same type.
What you can do is have a list or queue of a type-erasing wrapper such as std::variant or std::any which can contain objects of a type from correspondingly limited, or unlimited selection.
Based on your comments, I suspect you want to do something like that:
#include <iostream>
#include <string>
#include <list>
#include <memory>
class Shape
{
public:
virtual double area() const = 0;
};
class Circle : public Shape
{
double radius;
public:
Circle(const double r) : radius(r) {}
double area() const {
return 3.1416 * radius * radius;
}
};
class Square : public Shape
{
double side;
public:
Square(const double s) : side(s) {}
double area() const {
return side * side;
}
};
int main()
{
std::list<std::unique_ptr<Shape>> my_list;
my_list.push_back(std::make_unique<Circle>(2.0));
my_list.push_back(std::make_unique<Square>(4.0));
for(const auto& el : my_list)
std::cout << el->area() << std::endl;
}
Where I used polymorphism/inheritance to create an object of type Shape. Then Im using a list of that parent object as pointer, so it possible to create different child elements such as Cirlce or Square.
Yes there are ways to do this. This utilities both runtime and compile time polymorphism. My favorite utilities the following types.
C++ like pseudo-code follows:
#include <typeinfo>
namespace dynamics {
class AnyBase {
virtual const type_info& getTypeInfo();
virtual ~AnyBase()
}
template<typename T>
class DynamicAny : public AnyBase {
type_info typekind;
T val;
template<typename T>
DynamicAny(const T& t) : typekind(typeof(T)), val(t) {}
//... usual move/copy constructors/operators follow
//conversion operators
inline operator T&() {
return val;
}
inline operator const T&() const {
return val;
}
//possibly comparison operators with const T& too...
//virtual call for type information for this type
const type_info& getTypeInfo() {
return typekind;
}
}
}
You could then include any std::unique_ptr<AnyBase> within a vector or queue or any data structure for that matter. But assign it a DynamicAny<T> of the desired type. Then when you get it out of that data structure simply call getTypeInfo() on the AnyBase to determine which DynamicAny<T> to cast it to.
See type_info for more details.
http://www.cplusplus.com/reference/typeinfo/type_info/
You could also use std::any that does a similar thing.
https://en.cppreference.com/w/cpp/utility/any
c++17 only onward.
typedef std::queue<std::unique_ptr<AnyBase>> queue_type; //queue of unique_ptr of DynamicAny<T>
typedef std::list<queue_type> list_type; //list of queues of (only) strings
I am trying to solve this problem. I came up with this solution:
typedef unordered_map<string, double> stockDictType;
class StockTicker {
class Comparator {
public:
inline bool operator() (const string &a, const string &b) const {
return stocksDict.at(a) < stocksDict.at(b);
}
};
stockDictType stocksDict;
map<string, stockDictType::iterator, Comparator> stocksTicker; // this is where I need a custom comparator method
int tickerSize;
public:
StockTicker(int k): tickerSize(k) {}
// some other methods
};
As is evident, this fails to compile: StockTicker::stocksDict is not a static member. Now I can't make it so because I could require multiple instances of the StockTicker class.
std::map uses a strict comparator function parameter definition (std::map will only pass in the keys to be compared), so I can't overload it to pass a reference to the current instance of the StockTicker class (which I could've used to get access to StockTicker::stocksDict through public getters)
I took inspiration from this SO question and the subsequent answer to do this:
typedef unordered_map<string, double> stockDictType;
class StockTicker {
class Comparator {
public:
stockDictType &_stockDictRef;
explicit Comparator(stockDictType &stocksDict): _stockDictRef(stocksDict) {}
inline bool operator() (const string &a, const string &b) const {
return _stockDictRef.at(a) < _stockDictRef.at(b);
}
};
stockDictType stocksDict;
map<string, stockDictType::iterator, Comparator> stocksTicker(Comparator{stocksDict});
int tickerSize;
public:
StockTicker(int k): tickerSize(k) {}
void addOrUpdate(string name, double price) {
stocksDict[name] = price;
stocksTicker.at(name) = stocksDict.find(name);
}
vector<stockDictType::iterator> top() {
vector<stockDictType::iterator> ret(tickerSize);
auto it = stocksTicker.begin();
for(int i = 0; i < tickerSize; i++, it++)
ret[i] = it->second;
return ret;
}
};
This won't compile either. I get this kind of error in the StockTicker::addOrUpdate() and StockTicker::top() methods:
error: '((StockTicker*)this)->StockTicker::stocksTicker' does not have class type.
I tried a bunch of other stuff too (like declaring a public comparator method in the StockTicker class itself and trying to pass a function pointer of it to std::map. That failed as well; StockTicker::stocksTicker gets declared before the comparator method does and the compiler complains).
Any ideas on how to go about fixing this?
std::map<std::string, stockDictType::iterator, Comparator> stocksTicker(Comparator(stocksDict));
this defines a member function named stocksTicker that takes a stocksDict argument of type Comparator and returns a std::map.
std::map<std::string, stockDictType::iterator, Comparator> stocksTicker{Comparator{stocksDict}};
This defines a member variable stocksTicker which, by default, is initialized with a Comparator, which in turn was initialized with the member variable stocksDict.
I assume you want the second.
Your syntax was partway between the two. Whatever compiler you had got confused by this.
Live example
You should StockTicker(StockTicker &&)=delete and StockTicker& operator=(StockTicker &&)=delete, as maps containing references to their containing class are not safe to move or copy.
Generating an efficient move here is tricky. I suspect C++17 node splicing might make it possible to do it. You might have to embed a std::shared_ptr<stocksDict*> (yes, a shared pointer to a pointer), and use .key_comp to reseat the stocksDict in the target.
Compiling my code that contains this class:
class Dessin
{
private:
vector<Figures*>T;
public:
void ajouteFigure(const Figures& f) const
{
for(auto element: T)
{
T.push_back(f);
}
}
};
yields an error:
[Error] no matching function for call to
'std::vector::push_back(const Figures&) const'
This is what I'm supposed to do in the main()
Dessin s;
s.ajouteFigure(Cercle(1.1));
Why wouldn't this work?
Assuming Cercle is a class name, you're trying to push a value where a pointer is expected.
To "fix" the error you should change your ajouteFigure prototype to accept Figures pointers and non-const this:
void ajouteFigure(Figures* f)
Then you should call it passing a pointer to a Figures object, i.e. created with a new expression:
s.ajouteFigure(new Cercle(1.1));
That being said, this code seems pointless. You're adding the pointer as many times as you have elements in the vector (which is always 0 in the example you provided).
Using raw pointers is also unadvised, you should use smart pointers like std::unique_ptr, although that would break the current code.
Consider this, less improper, example:
class Dessin
{
private:
vector<unique_ptr<Figures>> T;
public:
void ajouteFigure(unique_ptr<Figures> f)
{
T.push_back(move(f)); // just once
}
};
and at the call site:
Dessin s;
s.ajouteFigure(make_unique<Cercle>(1.1)); // C++≥14
or, if you can't use C++14:
Dessin s;
s.ajouteFigure(unique_ptr<Figures>(new Cercle{1.1}));
Just to add to this, I think you would be better to make it a template function and create the right object inside the function with arguments to the constructor passed as function parameters.
This way you don't have to create a std::unique_ptr or use new every time you call the function.
Here's a basic implementation:
class Dessin{
public:
template<typename T, typename ... Args>
void ajouteFigure(Args &&... args){
figures.emplace_back(new T(std::forward<Args>(args)...));
}
private:
std::vector<std::unique_ptr<Figures>> figures;
};
Then using the class is less error-prone:
int main(){
Dessin d;
d.ajouteFigure<Cercle>(1.1);
}
I want to implement a container of generic types of element; and I want this container to support comparer.
When using STL's priority queue, I could define a comparer and initialise my priority queue container instance like this:
bool IsMyItemLarger(MyItem* a, MyItem* b) {
if(a && b && a->value > b->value)
return true;
return false;
}
std::priority_queue<
MyItem*,
vector<MyItem*>,
std::function<bool(MyItem*, MyItem*)>
> pq(IsMyItemLarger);
Now, I'd like to define my own container class, I'd like it to support any type including primitive type, so a template T should be in the class definition.
template<typename T...>
class MyContainer {
public:
vector<T*> items;
};
However, how can I define the comparer stub in my container class? (so that the client can pass e.g. "bool(MyItem*, MyItem*)" as a template argument and "IsMyItemLarger" as an argument of constructor?)
Note: (something off topic below!!)
I know how to implement SIMILAR class via interface, e.g.
I defined an interface and only items conforms to this interface can be insert into the container. It could pass compile (following code)
class AbstractBaseItem {
public:
virtual int compareTo(AbstractBaseItem* a) = 0;
};
class MyContainer {
public:
vector<AbstractBaseItem*> items;
bool greater(int i, int j) {
return items[i]->compareTo(items[j]) > 0;
}
};
class MyItem : public AbstractBaseItem {
public:
int value;
int compareTo(AbstractBaseItem* a) {
int aValue = ((MyItem*)a)->value;
if(value > aValue)
return 1;
else if(value < aValue)
return -1;
else
return 0;
}
};
However, logically, I don't want to use virtual functionality. All items in the container should be exactly the same type, not "as-is" type. E.g. I just want MyItem be in the container, not MyItem, YourItem, HisItem etc although these class all inherits from AbstractBaseItem. And it could be a primitive type like int, long, double...
So a template is actually what I wanted.
The comparison is delegated to another templated type, for convenience often left defaulted to std::greater<T> which does exactly what it seems to do; it's already available in the standard library, along with many other operations like less, greater_equal and so on, so no need to rewrite them. They're called operator wrappers and are available in
<functional>.
Therefore, have
template<typename T, typename Compare>
// ^^^^^^^^^^^^^^^^
class MyContainer {
vector<T*> items;
/* public: */Compare compare;
// ...
{
if ( compare(T1, T2) )
}
};
The signature for Compare must be
template <class T> bool cmp(const T&, const T&);
or similar in order to keep a standard design, where cmp(a, b) is expected to be a valid expression; of course, yours may different. More info about it here.
Containers generally allow you to use the comparator object they received, so you may want to make it public as well.
Tips, if I may:
Keep a vector<T>: pointers are bad; if you have to use them, prefer smart pointers.
You don't need std::function, just a callable object (plain functions, lambda ecc.)
I am working with std::priority_queue for the first time for a University assignment. The assignment is a simulation of process scheduling. I would like to pass a parameter to my Comparison struct constructor to initialise and I thought I saw it on another forum, but I'm unable to find the source again. I looked on SO before posting but I didn't see anything similar.
Here is my priority_queue:
/* schedules.hpp / .cpp */
#include "process.hpp"
namespace my = procschedassignment;
int tick = 0;
std::priority_queue<my::Process, _
std::vector<my::Process>,
PrioritiseHighestResponseRatioNext(tick) > rq;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
line 100 - compiler errors are here
// ...
Here is my Comparison struct:
/* prioritise_process.hpp / .cpp */
#include "process.hpp"
namespace my = procschedassignment;
struct PrioritiseHighestResponseRatioNext {
public:
explicit PrioritiseHighestResponseRatioNext(int const& cpu_time)
: cpu_time_(cpu_time) {};
bool PrioritiseHighestResponseRatioNext::operator()(my::Process const& lhs,
my::Process const& rhs) {
bool ret;
if (lhs.wait_time() > cpu_time_) {
ret = (lhs.ResponseRatio() > rhs.ResponseRatio());
} else if (rhs.wait_time() > cpu_time_) {
ret = (lhs.ResponseRatio() < rhs.ResponseRatio());
}
return ret;
};
private:
const int cpu_time_;
};
The compiler errors I get with this code are:
../src/schedules.cpp:100: error: ‘time’ cannot appear in a constant-expression
../src/schedules.cpp:100: error: template argument 3 is invalid
../src/schedules.cpp:100: error: invalid type in declaration before ‘;’ token
Is it possible to have a parameterised Comparison struct with std::priority_queue?
I am new to STL so I apologise that I don't have a better understanding of what's happening here.
You are trying to pass an object as template-parameter. This will not work. You should provide your comparator as argument to the constructor, and the type of the comparator as template-argument.
// declare type
typedef std::priority_queue<my::Process,
std::vector<my::Process>,
PrioritiseHighestResponseRatioNext > process_queue;
// ^^^ just a type, no object ^^^
// create object
process_queue rq(PrioritiseHighestResponseRatioNext(tick));