The task is to write a function filter that takes list (std::list) as the first argument and predicate (std::function) as the second argument. The function needs to return new list which contains all the elements that meet the condition from the predicate.
Then the function needs to be improved with:
std::list with arbitrary type
arbitrary container with arbitrary type
I easily did the main part and first improvement, but could not find the solution for the second improvement even after several days of research. Here is the code for the first improvement (this code works normally):
#include <iostream>
#include <list>
#include <functional>
template <typename T>
std::list<T> filter (std::list<T>& l, std::function<bool(T)> p){
for (auto it = l.begin(); it != l.end(); ++it){
if (p(*it)){
continue;
}
else {
it = l.erase(it);
}
}
return l;
}
int main()
{
std::list<int> initial_list;
for (int i = 0; i <= 50; ++i){
initial_list.push_back(i);
}
std::function<bool(int)> predicate = [](const int& a){
return a % 2 == 0;
};
std::list<int> end_list = filter(initial_list, predicate);
for (auto e : end_list){
std::cout << e << " ";
}
std::cout << std::endl;
return 0;
}
Here is the code I tried for the second improvement:
template <typename T, typename U>
T<U> filter (T<U>& l, std::function<bool(U)> p){
for (auto it = l.begin(); it != l.end(); ++it){
if (p(*it)){
continue;
}
else {
it = l.erase(it);
}
}
return l;
}
But vim says expected unqualified-id and compiler says:
problem5.cpp:6:1: error: âTâ is not a template
T<U> filter (T<U>& l, std::function<bool(U)> p){
^
problem5.cpp:6:14: error: âTâ is not a template
T<U> filter (T<U>& l, std::function<bool(U)> p){
^
Thank you in advance.
You need a template template parameter to make it compile i.e.
template <template<typename> typename T, typename U>
T<U> filter (const T<U> &l) {
}
Related
I am making function Fold, which can accept different classes, e.g. Sum, which specify the type of operation done.
#include <iostream>
#include <vector>
#include <list>
template <typename T>
struct Sum {
public:
auto operator() (T init, std::list<int>::const_iterator first,
std::list<int>::const_iterator last) {
for (auto it = first; it != last; ++it) {
init += *it;
}
return init;
}
};
template <typename Iterat, typename T, typename Operator>
T Fold(Iterat first, Iterat last, T init, Operator func) {
return func(init, first, last);
}
int main() {
std::list<int> a{1, 2, 3, 6};
std::cout << Fold(a.cbegin(), a.cend(), 0, Sum()) << std::endl;
return 0;
}
However, when I ran the code, I got the mistake "no viable constructor or deduction guide for deduction of template arguments of 'Sum'"
The mistake can be managed two ways:
If I use "int" instead of "template T" in class sum.
If I specify in main() the type I want to use like:
Fold(a.cbegin(), a.cend(), 0, Sum<int>())
Are there other ways to do something with this mistake? Neither of the two solutions I showed higher are suitable for my task
Template arguments are deduced from the (function) arguments. Since Sum does not have arguments, the template arguments cannot be deduced. This is explained here.
All is not lost, however. Sum, as a class, does not use the template parameters. Only the operator() method does. Therefore, you can put template only the operator() method, and leave Sum as a non-template class.
For instance:
struct Sum {
public:
template <typename T>
auto operator() (T init, std::list<int>::const_iterator first,
std::list<int>::const_iterator last) {
for (auto it = first; it != last; ++it) {
init += *it;
}
return init;
}
};
I would also go as far as templating the iterator as well, like you did with Fold. The complete code example, with some additional usage examples, is:
#include <functional>
#include <iostream>
#include <list>
#include <vector>
struct Sum {
public:
template <typename T, typename Iterat>
auto operator() (T init, Iterat first, Iterat last) {
for (auto it = first; it != last; ++it) {
init += *it;
}
return init;
}
};
template <typename Iterat, typename T, typename Operator>
T Fold(Iterat first, Iterat last, T init, Operator func) {
return func(init, first, last);
}
int main() {
std::list<int> a{1, 2, 3, 6};
std::cout << Fold(a.cbegin(), a.cend(), 0, Sum()) << std::endl;
// init is a float. While the iterators return int.
std::cout << Fold(a.cbegin(), a.cend(), 0.5 , Sum()) << std::endl;
std::list<std::string> b{"1", "2", "3", "6"};
std::cout << Fold(b.cbegin(), b.cend(), std::string(""), Sum()) << std::endl;
return 0;
}
As an exercise I'm trying to implement Python's str.join method in C++. I will eventually add the function as a method of the std::string class but I figure getting it to work is more of a priority. I've defined the function as follows:
template<typename Iterable>
std::string join(const std::string sep, Iterable iter);
Is there any way that I can ensure that the Iterable type is actually iterable? E.g. I wouldn't want to receive an int or char..
In C++, rather than having one Iterable, we pass in an iterator (almost a pointer) to the front and the end of the range:
template<typename Iter>
std::string join(const std::string &sep, Iter begin, Iter end);
Note that the sep should be passed as const reference, as you don't need to copy it.
You don't need to worry about whether the Iter is actually an iterator, though. This is because the code will simply fail to compile if it doesn't work.
For example, suppose you implemented it like so (this is a bad implementation):
template<typename Iter>
std::string join(const std::string &sep, Iter begin, Iter end) {
std::string result;
while (begin != end) {
result += *begin;
++begin;
if (begin != end) result += sep;
}
return result;
}
Then the type passed in as Iter must have an operator++, an operator!=, and an operator* to work, which is the well understood contract of an iterator.
All standard c++ collections has begin() and end() member functions. You could make use of that fact to ensure that the passed argument is actually a collection (in your terminology - iterable) by some SFINAE (c++11 example):
#include <array>
#include <list>
#include <vector>
#include <map>
#include <string>
template <class Iterable>
auto join(const std::string sep, const Iterable& iterable) -> decltype(iterable.begin(), iterable.end(), std::string{}) {
(void)sep; // to suppress warning that sep isn't used
// some implementation
return {};
}
int main() {
join(";", std::array<int, 5>{});
join(";", std::list<int>{});
join(";", std::vector<float>{});
join(";", std::string{});
join(";", std::map<int, float>{});
//join(";", int{}); // does not compile as int is not a collection
}
[live demo]
You may use template template syntax and - if needed - use SFINAE to make sure that proper class members are existing:
#include <vector>
#include <list>
#include <string>
#include <map>
#include <ostream>
//! Single value containers.
template< template<class> class L, class T,
class EntryT = typename L<T>::value_type>
std::string my_join(const std::string_view sep, const L<T>& anyTypeIterable)
{
std::stringstream ss;
bool first = true;
for (const EntryT& entry : anyTypeIterable)
{
if (first) first = false;
else ss << sep;
ss << entry;
}
return ss.str();
}
//! std::map specialization - SFINAE used here to filter containers with pair value_type
template< template<class, class> class L, class T0, class T1,
class EntryT = typename L<T0, T1>::value_type,
class FirstT = typeof(EntryT::first),
class SecondT = typeof(EntryT::second)>
std::string my_join(const std::string_view sep, const L<T0, T1>& anyTypeIterable)
{
std::stringstream ss;
bool first = true;
for (const EntryT& entry : anyTypeIterable)
{
if (first) first = false;
else ss << sep;
ss << entry.first << sep << entry.second;
}
return ss.str();
}
int main()
{
std::cout << my_join("; ", std::vector<int>({1, 2, 3, 4})) << std::endl;
std::cout << my_join("; ", std::list<int>({1, 2, 3, 4})) << std::endl;
std::cout << my_join("; ", std::string("1234")) << std::endl;
std::cout << my_join("; ", std::map<int, int>({ {1, 2}, {3, 4} })) << std::endl;
return 0;
}
// Output:
// 1; 2; 3; 4
// 1; 2; 3; 4
// 1; 2; 3; 4
// 1; 2; 3; 4
From https://devblogs.microsoft.com/oldnewthing/20190619-00/?p=102599 :
template<typename C, typename T = typename C::value_type>
auto do_something_with(C const& container)
{
for (int v : container) { ... }
}
Or if the container doesn't implement value_type:
template<typename C, typename T = std::decay_t<decltype(*begin(std::declval<C>()))>>
auto do_something_with(C const& container)
{
for (int v : container) { ... }
}
Or if you want only containers containing types convertible to int:
template<typename C, typename T = std::decay_t<decltype(*begin(std::declval<C>()))>,
typename = std::enable_if_t<std::is_convertible_v<T, int>>>
auto do_something_with(C const& container)
{
for (int v : container) { ... }
}
But see the comments in that blog post for more refinements.
I would like to query a lambda/function about how much parameters it uses.
Small (pseudo) example code:
template <class InputIterator, class Predicate>
bool visitAll( InputIterator f, InputIterator l, Predicate p, UserData* d=nullptr)
{
for(; f != l; ++f)
{
if(number_of_arguments(p) == 1)
{
if(!p(*f))
return false;
}
else
{
if(!p(*f, *d))
return false;
}
}
}
Note that the function I am asking for is number_of_arguments(...).
I have been searching in the Closure and std::function reference, but did not find a clue about a solution.
Thanks for your help!
Obviously the code as you posted does not make much sense, as anyway either p(*f) or p(*f, *d) would fail to compile. Therefore you need to split this into two templates, and then you can test the number of arguments of Predicate using a rather straightforward SFINAE approach:
template <class InputIterator, class Predicate>
bool visitAll( InputIterator f, InputIterator l, Predicate p, UserData* d=nullptr,
decltype(declval<Predicate>()(*declval<InputIterator>()),1) unused = 1)
{
cout << "1" << std::endl;
for(; f != l; ++f)
{
if(!p(*f))
return false;
}
return true;
}
template <class InputIterator, class Predicate>
bool visitAll( InputIterator f, InputIterator l, Predicate p, UserData* d=nullptr,
decltype(declval<Predicate>()(*declval<InputIterator>(), declval<UserData>()),1) unused = 1)
{
cout << "2" << std::endl;
for(; f != l; ++f)
{
if(!p(*f, *d))
return false;
}
return true;
}
Usage:
std::vector<int> a{1,2};
const auto t = [](int x){ return 1;};
const auto t2 = [](int x, UserData y){ return 1;};
UserData d;
visitAll(a.begin(), a.end(), t);
visitAll(a.begin(), a.end(), t2, &d);
Of course, you can use std::bind to avoid code duplication by calling first version from the second.
Another approach is to use code similar to how std::bind checks that it got needed number of arguments:
template<typename _Func>
struct noa_helper {
};
template<typename _Ret, typename... _Args>
struct noa_helper<_Ret (*)(_Args...)> {
static int noa() { return sizeof...(_Args); }
};
template<class F>
int number_of_arguments(F f) {
return noa_helper<typename std::decay<F>::type>::noa();
}
void foo();
int bar(int x, int y);
...
std::cout << number_of_arguments(foo) << std::endl; // prints 0
std::cout << number_of_arguments(bar) << std::endl; // prints 2
This works only for real functions, not lambdas, nor std::function, though probably some more template magic can make it work for the latter two categories.
I want to create a template method for adding and counting of elements in a map (stl) independently from element type. The question is: can I use a template for the iterator type as shown below?
template < typename Type, typename Iter >
void TextStat::addElement(Type element, map<Type, int> map, Iter &it) {
it = map.find(element);
if ( it == map.end() ) {
map.insert(pair<Type, int>(element, 1));
} else {
it->second += 1;
}
}
You may write your method like this:
template <typename Type>
void TextStat::addElement(const Type& element, std::map<Type, int>& m) {
std::map<Type, int>::iterator it = m.find(element);
if (it == m.end()) {
m.insert(std::pair<Type, int>(element, 1));
} else {
it->second += 1;
}
}
or even simpler, as default value initialization of int is 0:
template <typename Type>
void TextStat::addElement(const Type& element, std::map<Type, int>& m) {
m[element]++;
}
I'm trying to move each element that has x value to the beginning of vector so that all the element that has x value is at the front of the vector, but it is not working , so can you tell me what I've done wrong, please?
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
template <typename Container, typename Arg>
void move_x(Container& c, Arg x)
{
typename Container::iterator it = find(c.begin(), c.end(), x);
if (it!=c.end()) {
c.insert(c.begin(), *it);
remove (it, c.end(), x);
}
}
int main()
{
int x=1;
vector <int> v{1,2,4,6,7,1,3,1,1,8,9};
move_x(v, x);
for(auto i:v)
cout<<v[i];
return 0;
}
and I'm getting this output when I run it
411613848811
Once you insert into the container, the iterator is no longer valid
c.insert(c.begin(), *it); // This invalidates 'it'
remove (it, c.end(), x); // oops! trying to use invalid iterator
Using std::rotate provides better alternative, which doesn't invalidate the iterators:
template <typename Container, typename Arg>
void move_x(Container& c, Arg x)
{
typedef typename Container::iterator It;
It write_it = c.begin(), read_it = c.begin();
for (;;) {
It found_it = find(read_it, c.end(), x);
if (found_it==c.end()) break;
read_it = found_it;
++read_it;
std::rotate(write_it,found_it,read_it);
++write_it;
}
}
As long as you are dealing with simple items like ints, this is a good approach:
template <typename Container, typename Arg>
void move_x(Container& c, Arg x)
{
typename Container::reverse_iterator it = std::remove(c.rbegin(),c.rend(),x);
for (;it!=c.rend();++it) {
*it = x;
}
}
This is a fixed implementation, of what you had in your code:
template <typename Container, typename Arg>
void move_x(Container& c, Arg x)
{
typename Container::iterator it = find(c.begin(), c.end(), x);
if (it!=c.end()) {
c.erase(it);
c.insert(c.end(), x);
}
}
One issue with your implementation is that insert anywhere but the end can cause a reallocation and regardless will invalidate anything after the inserted position. So by definition since you are inserting at the beginning it will not be valid.
The second issue is with cout<<v[i]; it should actually be cout<<i;.
A nicer implementation that uses reverse iterators and moves all xs. This one erases as it goes and keeps a count and then does count inserts when done. Using erase with reverse iterators is a little tricky:
template <typename Container, typename Arg>
void move_all_x(Container& c, Arg x)
{
unsigned int count = 0 ;
for( typename Container::reverse_iterator it = c.rbegin() ; it != c.rend(); )
{
if( *it == x )
{
c.erase(--(it++).base() ) ;
++count ;
}
else
{
++it ;
}
}
for( unsigned int i = 0; i < count; ++i )
{
c.insert(c.begin(), x) ;
}
}
You could use std::partition, which will move all elements in a range which meet a given predicate to the beginning of the range, and return an iterator to the first element which doesn't meet the predicate.
template <typename Container, typename Arg>
void move_x(Container& c, Arg x)
{
typename Container::iterator endrange =
std::partition(c.begin(), c.end(), [&x](Arg ele){ return ele == x; });
}
In this case, we're not using the return value but I think it would probably be useful.
The output is wrong. There should be for (auto i:v) cout << i; not v[i]. You would see the garbage with a right algorithm too
You need a loop to process all matches (or use count and insert). v defined as list<int>:
template <typename Container, typename Arg>
void move_x(Container& c, Arg x)
{
for (auto it = find(c.begin(), c.end(), x);
it != c.end();
it = find(it, c.end(), x)) {
c.insert(c.begin(), x);
it = c.erase(it);
}
}