I wanted to write a template function for printing increasing and decreasing priority queues. Currently I have implemented it as
void print_queue1(priority_queue<int> q, string s){
cout << "Value of prior queue " << s << " is : [";
while(!q.empty()){
cout << " " << q.top() << ",";
q.pop();
}
cout << "]" << endl;
}
// min-heap
void print_queue2(priority_queue<int, vector<int>, greater<int>> q, string s){
cout << "Value of prior queue " << s << " is : [";
while(!q.empty()){
cout << " " << q.top() << ",";
q.pop();
}
cout << "]" << endl;
}
Is there some way to write a single template function that can do this?
You can use a variadic function template for this. Since the logic is the same regardless of the queue type, we can just accept any type of queue like
template <typename... Params>
void print_queue(priority_queue<Params...> q, string s){
cout << "Value of prior queue " << s << " is : [";
while(!q.empty()){
cout << " " << q.top() << ",";
q.pop();
}
cout << "]" << endl;
}
Here, Params will be deduced from the template parameters of the supplied priority_queue for you by the compiler and it will stamp out a concrete function for each different parameter set.
The template class std::priority_queue uses three template type parameters.
template<
class T,
class Container = std::vector<T>,
class Compare = std::less<typename Container::value_type>
> class priority_queue;
You may use those same three parameters in your function to accept any instantiation of std::priority_queue.
template<class T, class Container, class Compare>
void print_queue(priority_queue<T,Container,Compare> q, string s){
cout << "Value of prior queue " << s << " is : [";
while(!q.empty()){
cout << " " << q.top() << ",";
q.pop();
}
cout << "]" << endl;
}
Or, you may remove/restrict any one of them to enforce a subset of priority queues..
template<class Container, class Compare>
void print_queue(priority_queue<int,Container,Compare> q, string s){
// This function is really only designed for priority queues of int!
cout << "Value of prior queue " << s << " is : [";
while(!q.empty()){
cout << " " << q.top() << ",";
q.pop();
}
cout << "]" << endl;
}
Related
I'm getting compiler error when using std::forward on a class template parameter, but not on a function template parameter. I'd like to know how to std::forward a class template parameter. This is my code:
#include <iostream>
#include <vector>
template<typename T>
class Data_List {
std::vector<T> data_list;
public:
Data_List() = default;
~Data_List() = default;
std::size_t get_list_size() {
return data_list.size();
}
void add_to_list(T&& data) {
std::cout << "\nbefore inside add_to_list: " << data.size() << std::endl;
data_list.push_back(std::forward<T>(data));
std::cout << "after inside add_to_list: " << data.size() << std::endl;
}
};
template<typename T>
void print(T&& t) {
}
int main() {
Data_List<std::vector<int>> list_of_data;
std::vector<int> data(100, 2);
std::cout << "\n1. before size: " << data.size() << std::endl;
std::cout << "1. before Data_List size: " << list_of_data.get_list_size() << std::endl;
print(data);
// list_of_data.add_to_list(data); // gives compiler error here
std::cout << "\n1. after size: " << data.size() << std::endl;
std::cout << "1. after Data_List size: " << list_of_data.get_list_size() << std::endl;
std::cout << "--------------------------------------------------------------------------\n" << std::endl;
std::cout << "\n2. before size: " << data.size() << std::endl;
std::cout << "2. before Data_List size: " << list_of_data.get_list_size() << std::endl;
print(std::move(data));
list_of_data.add_to_list(std::move(data));
std::cout << "\n2. after size: " << data.size() << std::endl;
std::cout << "2. after Data_List size: " << list_of_data.get_list_size() << std::endl;
std::cout << "--------------------------------------------------------------------------\n" << std::endl;
}
This is the error I'm getting cannot bind rvalue reference of type ‘std::vector<int>&&’ to lvalue of type ‘std::vector<int>’
In here:
void add_to_list(T&& data) {
std::cout << "\nbefore inside add_to_list: " << data.size() << std::endl;
data_list.push_back(std::forward<T>(data));
std::cout << "after inside add_to_list: " << data.size() << std::endl;
}
data isn't a forwarding reference, it's an rvalue reference, so you can't do something like list_of_data.add_to_list(data); because data is an lvalue.
To create a forwarding reference, the type must exist as a template parameter of the same function template (Give a reading to the forwarding references part of this cppreference.com page and look specifically at the first example):
function parameter of a function template declared as rvalue reference to cv-unqualified type template parameter of that same function template.
Basically, what you want to do is this:
// ...
template <typename U>
void add_to_list(U&& data) {
std::cout << "\nbefore inside add_to_list: " << data.size() << std::endl;
data_list.push_back(std::forward<U>(data));
std::cout << "after inside add_to_list: " << data.size() << std::endl;
}
// ...
Then data becomes a forwarding reference and does what is intended.
Demo
I have class Date, functions bubbleSort, isAfter and printVector. So my task is: Use the function bubbleSort to sort vector type objects Date(using function isAfter which compares dates). I've done something but it doesn't works, so can anyone help me with this?
Function bubble sort(doesn't works with "Date", works fine with integers ,strings...).
Here is my code:
//isAfter
template<>
bool isAfter(const Date &first, const Date &second) {
if (first.getYear() == second.getYear()) {
if (first.getMonth() == second.getMonth()) {
if (first.getDay() == second.getDay()) {
cout << first.toString() << " is equal to " << second.toString() << endl;
return false;
} else if (first.getDay() > second.getDay()) {
cout << " " << first.toString() << " is after " << " " << second.toString() << endl;
return true;
} else if (first.getDay() < second.getDay()) {
cout << " " << second.toString() << " is after " << " " << first.toString() << endl;
return true;
}
} else if (first.getMonth() > second.getMonth()) {
cout << " " << first.toString() << " is after " << " " << second.toString() << endl;
return true;
} else if (first.getMonth() < second.getMonth()) {
cout << " " << second.toString() << " is after " << " " << first.toString() << endl;
return true;
}
} else if (first.getYear() > second.getYear()) {
cout << " " << first.toString() << " is after " << " " << second.toString() << endl;
return true;
} else if (first.getYear() < second.getYear()) {
cout << " " << second.toString() << " is after " << " " << first.toString() << endl;
return true;
}
return false;
}
//bubbleSort
template<typename T>
void bubbleSort(vector<T> &vec) {
bool swapp= true;
while (swapp) {
swapp= false;
for (unsigned int i = 0; i < vec.size()- 1; i++) {
if (vec[i] > vec[i + 1]) {
swap(vec[i], vec[i + 1]);
swapp = true;
}
}
}
}
so how can i add isAfter in bubbleSort to work fine with "Date" objects?
If this is always the sort order for dates and you control that type, you could implement the comparison operators operator<, operator>, operator<=, operator>=, operator== and operator!= for that type.
Otherwise, the conventional approach is to modify your sorting algorithm to accept a custom comparator (by convention, having the interface of operator<, which requires you to flip your comparison) from its callers, something like:
template <typename T, typename Compare>
void bubbleSort(vector<T> &vec, Compare compare) {
// as you currently have, but using compare(a, b) instead of a < b
}
template <typename T>
void bubbleSort(vector<T> &vec) {
bubbleSort(vec, std::less<>());
}
Then callers can use isAfter like this:
bubbleSort(dates, [](const Date& a, const Date& b) { return isAfter(b, a); });
If we make a reference to a vector element and then resize the vector, the reference is no longer valid, the same happens with an iterator:
std::vector<int> vec{0, 1, 2, 3, 4, 5};
int& ref = vec[0];
auto itr = vec.begin();
cout << ref << " " << *itr << endl;
vec[0] = 7;
cout << ref << " " << *itr << endl;
vec.resize(100);
vec[0] = 3;
cout << ref << " " << *itr << endl;
Prints out:
0 0
7 7
0 0 // We expected a 3 here
And I know that it would be more practical to just keep a reference to the vector itself and call vec[0], but just for the sake of questioning, is it possible to keep an object that will always be vec[0] even if the object is moved?
I've tried writing a small helper class to help with this, but I'm unsure if this is the best method or if it can even fail?
template<typename T>
struct HelperClass
{
std::vector<T>& vec;
size_t element;
HelperClass(std::vector<T>& vec_, size_t element_) : vec(vec_) , element(element_) {}
// Either define an implicit conversion from HelperClass to T
// or a 'dereference' operator that returns vec[0]
operator T&() { return vec.at(element); }
T& operator*() { return vec.at(element); }
};
And use it by either the implicit conversion to T& or by the 'dereference' operator:
std::vector<int> vec{0, 1, 2, 3, 4, 5};
int& ref = vec[0];
auto itr = vec.begin();
HelperClass<int> hlp = HelperClass<int>(vec, 0); // HelperClass
cout << ref << " " << *itr << " " << hlp << " " << *hlp << endl;
vec[0] = 7;
cout << ref << " " << *itr << " " << hlp << " " << *hlp << endl;
vec.resize(100);
vec[0] = 3;
cout << ref << " " << *itr << " " << hlp << " " << *hlp << endl;
Which already prints what was excepted:
0 0 0 0
7 7 7 7
0 0 3 3
So is there a better way to do this aside from having a helper class and can the helper class be unreliable in some cases?
I've also come across this thread in reddit, but it seems that they do not discuss the helper class there
The one thing you could do is have a vector of pointers rather than a vector of instances. That of course has its own passel of issues but if you must have object references survive a vector resize that will do it.
Any reallocation of the vector will invalidate any pointers, references and iterators.
In your example, your HelperClass is useless in sense that this:
cout << ref << " " << *itr << " " << hlp << " " << *hlp << endl;
is the same as:
cout << ref << " " << *itr << " " << vec[0] << " " << vec[0] << endl;
If a reallocation happens, just use the iterator interface .begin() .end() to access again the iterators.
I wrote a function to print a given vector.
template <typename T>
void print_vector(std::string text, std::vector<T> &vect){
std::cout << ">>>>>>>>>> " << text << " <<<<<<<<<<" << std::endl;
for(T &t: vect){
std::cout << t << ", ";
}
std::cout << std::endl << "--------------------" << std::endl;
}
But when I give a vector of shared_ptr to the function, it prints the address, but not the pointed value.
Is there a way to print the value when the element is a shared_ptr..?
I tried the following way, but it gives me a compile error and I can't figure out how to fix it.
template <typename T, typename F>
void print_vector(std::string text, std::vector<T> &vect){
std::cout << ">>>>>>>>>> " << text << " <<<<<<<<<<" << std::endl;
for(T &t: vect){
if(std::is_same<T, std::shared_ptr<F>>::value) {
std::cout << *t << ", ";
} else {
std::cout << t << ", ";
}
}
std::cout << std::endl << "--------------------" << std::endl;
}
Overload your function for vectors of smart pointers.
template <typename T>
void print_vector(std::string text, std::vector<T> &vect){
std::cout << ">>>>>>>>>> " << text << " <<<<<<<<<<" << std::endl;
for(T &t: vect){
std::cout << t << ", ";
}
std::cout << std::endl << "--------------------" << std::endl;
}
template <typename T>
void print_vector(std::string text, std::vector<std::shared_ptr<T>> &vect){
std::cout << ">>>>>>>>>> " << text << " <<<<<<<<<<" << std::endl;
for(auto &t: vect){
std::cout << *t << ", ";
}
std::cout << std::endl << "--------------------" << std::endl;
}
If you have more cases where you wish to print something different, you may find that some of the overloads are ambiguous, you may have to disable the ambiguous templates when they should match each other.
You can overload operator for shared_ptr check out the following code:
template<typename T>
ostream& operator<<(ostream& out, const shared_ptr<T>& s_ptr)
{
if (s_ptr != nullptr)
out << (*s_ptr);
return out;
}
template <typename T>
void print_vector(std::string text, std::vector<T> &vect){
std::cout << ">>>>>>>>>> " << text << " <<<<<<<<<<" << std::endl;
for(T &t: vect){
std::cout << t << ", ";
}
std::cout << std::endl << "--------------------" << std::endl;
}
you want to overload the stream operator for shared_ptr, this could either be for a specific type or for all types:
template <typename T>
std::ostream& operator << (std::ostream& os, const std::shared_ptr< T >& p)
{
if ( p )
{
os << *p;
}
else
{
os << "<null>";
}
return os;
}
Just overload the function template:
template <typename T>
void print_vector(std::string text, std::vector<T> &vect){
std::cout << ">>>>>>>>>> " << text << " <<<<<<<<<<" << std::endl;
for(T &t: vect){
std::cout << t << ", ";
}
std::cout << std::endl << "--------------------" << std::endl;
}
template <typename T>
void print_vector(std::string text, std::vector<std::shared_ptr<T>> &vect) {
std::cout << ">>>>>>>>>> " << text << " <<<<<<<<<<" << std::endl;
for(auto &t: vect){
std::cout << *t << ", ";
}
std::cout << std::endl << "--------------------" << std::endl;
}
Now it will handle both cases.
The code below shows a multi_index container which is indexed by sequence and order.
In my use case elements will be mainly searched by index, and if existing, the next element (by order) is obtained.
My question is, how to get the rank (by order) of obtained next element?
#include <iostream>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/ranked_index.hpp>
#include <boost/multi_index/identity.hpp>
using namespace boost::multi_index;
typedef multi_index_container <
int
, indexed_by<
sequenced<>
, ordered_non_unique<identity<int>>
, ranked_non_unique<identity<int>>
>
> Ints;
int main() {
Ints ints;
auto & sequence=ints.get<0>();
auto & order=ints.get<1>();
sequence.push_back(2);
sequence.push_back(-1);
sequence.push_back(5);
sequence.push_back(6);
auto it = order.find(2);
if (it!=order.end()) {
std::cout
<< "next to "
<< *it
<< " by sequence is "
<< *(++(ints.project<0>(it)))
<< std::endl
;
std::cout
<< "next to "
<< *it
<< " by order is "
<< *(++(ints.project<1>(it))) //++it is good too
<< std::endl
;
std::cout
<< "rank of next by sequence is "
// << ??? ints.rank<???>(???)
<< std::endl
;
std::cout
<< "rank of next by order is "
// << ??? ints.rank<???>(???)
<< std::endl
;
}
}
#sehe's answer is perfectly valid but runs in linear time. If you want better performance, consider defining your index #0 as random_access and #1 as ranked_non_unique (index #2 is redundant):
typedef multi_index_container <
int
, indexed_by<
random_access<>
, ranked_non_unique<identity<int>>
>
> Ints;
so that you can write:
std::cout
<< "rank of next by sequence is "
<< ints.project<0>(it)-sequence.begin()+1 // O(1)
<< std::endl
;
std::cout
<< "rank of next by order is "
<< order.rank(it)+1 // O(log n)
<< std::endl
;
Assuming you want some kind of "index into" or "offset from begin" in the sequenced index:
if (it!=order.end()) {
auto rank_of = [&](auto it) {
return std::distance(sequence.begin(), ints.project<0>(it));
};
auto seq_next = std::next(seq_it);
auto ord_next = std::next(it);
if (seq_next!=sequence.end())
{
std::cout << "next to " << *it << " by sequence is " << *seq_next << std::endl;
std::cout << "rank of next by sequence is " << rank_of(seq_next) << std::endl;
}
if (ord_next!=order.end())
{
std::cout << "next to " << *it << " by order is " << *ord_next << std::endl ;
std::cout << "rank of next by order is " << rank_of(ord_next) << std::endl;
}
}
Without polymorphic lambdas you should write it out