I am trying to write a simple template function that prints every element of some container, without using for loops. So far, I have
#include <iostream>
#include <vector>
#include <algorithm>
template <typename T> void print_with_space(T x){
std::cout << x << ' ';
}
template <typename T> void print_all(T beg, T end){
std::for_each(beg, end, print_with_space<int>);
std::cout << '\n';
}
int main(){
int a[] = {1, 2, 3};
std::vector<int> v(a, a+3);
print_all(v.begin(), v.end());
return 0;
}
The code compiles and runs, but only because I put print_with_space<int> inside the implementation of print_all. I would like to just have print_with_space there for obvious reasons, but then the code doesn't compile. How do I do this?
You can use:
std::for_each(beg, end, [](const typename T::value_type& value) {
print_with_space(value);
});
T is of type std::vector<>::iterator, which is a RandomAccessIterator. Every RandomAcessIterator has a underlying type, which is exposed by value_type.
So, if you pass std::vector<int>::iterator, std::vector<int>::iterator::value_type would be an int.
Now that you have the type, you can make a lambda, which will get executed for every iteration.
In C++14, you can even do:
//'auto' automatically deduces the type for you
std::for_each(beg, end, [](const auto& value) {
print_with_space(value);
});
Another option:
template <typename T> void print_all(T beg, T end) {
std::for_each(beg, end, print_with_space<decltype(*beg)>);
std::cout << '\n';
}
Alternative for C++03:
#include <iterator>
template <typename T> void print_all(T beg, T end)
{
typedef typename std::iterator_traits<T>::value_type val_t;
std::for_each(beg, end, print_with_space<val_t>);
std::cout << '\n';
}
The most flexible solution, which will work with all versions of c++, is to make print_with_space a function object.
This confers a number of advantages:
no need to specify template type at call site.
no need to fiddle around with manual type deduction.
partial specialisation can be achieved by having the functor defer to a templated free function.
Such as:
#include <iostream>
#include <iomanip>
#include <vector>
#include <algorithm>
// basic implementation
template<class T> void impl_print_with_space(const T& x)
{
std::cout << x << ' ';
}
// what about special handling for strings?
template<class C, class Ch, class Alloc>
void impl_print_with_space(const std::basic_string<C, Ch, Alloc>& x)
{
std::cout << std::quoted(x) << ' ';
}
// functor
struct print_with_space
{
template<class T> void operator()(const T& x) const
{
impl_print_with_space(x);
}
};
template <typename Iter> void print_all(Iter beg, Iter end)
{
std::for_each(beg, end, print_with_space());
std::cout << '\n';
}
int main(){
int a[] = {1, 2, 3};
std::vector<int> v(a, a+3);
print_all(v.begin(), v.end());
auto b = std::vector<std::string> { "hello", "world" };
print_all(b.begin(), b.end());
return 0;
}
Related
I was trying to make a single function that takes in a container and implicitly have it convert to a boost::iterator_range as I thought that was it's purpose, but it seems that I'm missing something.
Here's an example of what I was thinking:
#include <boost/range/iterator_range.hpp>
#include <vector>
template<typename IT>
void fn_x(boost::iterator_range<IT>) {
}
void fn_y() {
std::vector<int> a(64);
fn_x(boost::make_iterator_range(a.begin(), a.end())); // Works
fn_x(a); // Doesn't
}
Demo
So how would I get fn_x to accept both a container and a range object, in the same function?
Sorry, forgot to mention that I'm using c++14.
The "obvious" answer is to be less specific: Live On Coliru
#include <boost/range/iterator_range.hpp>
#include <iostream>
#include <numeric>
#include <vector>
template <typename Range> auto fn_x(Range&& r) {
using std::begin;
using std::end;
return accumulate(begin(r), end(r), 0.0);
}
int main() {
std::vector<int> a{1,2,3};
std::cout << fn_x(boost::make_iterator_range(a.begin(), a.end())) << "\n";
std::cout << fn_x(a) << "\n";
}
Prints 6 two times.
If you really want to be more specific consider:
std::span (c++20) - which has implicit conversions from e.g. std::vector
range concepts (c++20)
if all else fails, sfinae:
template <typename It> auto fn_x(boost::iterator_range<It> r) {
return accumulate(r.begin(), r.end(), 0.0);
}
template <typename SomeOtherRange,
typename Enable =
decltype(boost::make_iterator_range(std::begin(std::declval<SomeOtherRange>()),
std::end(std::declval<SomeOtherRange>())))>
auto fn_x(SomeOtherRange const& r) {
return fn_x(boost::make_iterator_range(std::begin(r), std::end(r)));
}
See it Live On Coliru (c++11 compatible)
#include <boost/range/iterator_range.hpp>
#include <iostream>
#include <numeric>
#include <vector>
template <typename It> double fn_x(boost::iterator_range<It> r) {
return accumulate(r.begin(), r.end(), 0.0);
}
template <typename SomeOtherRange,
typename Enable =
decltype(boost::make_iterator_range(std::begin(std::declval<SomeOtherRange>()),
std::end(std::declval<SomeOtherRange>())))>
double fn_x(SomeOtherRange const& r) {
return fn_x(boost::make_iterator_range(std::begin(r), std::end(r)));
}
int main() {
std::vector<int> a{1, 2, 3};
std::cout << fn_x(boost::make_iterator_range(a.begin(), a.end())) << "\n";
std::cout << fn_x(a) << "\n";
}
Still printing
6
6
#include <iterator>
template<typename IT>
void fn_x(boost::iterator_range<IT>) {
}
template<typename Range>
void fn_x(Range r) {
fn_x(boost::make_iterator_range(std::begin(r), std::end(r));
}
I'm assuming that Range will come with a begin and end method
This should be a comment, but I don't have enough reputation.
If you have access to c++20 you could try std::span.
Look at this monstrosity:
#include <algorithm>
#include <iostream>
#include <span>
#include <vector>
template <typename T>
struct ExistsFunctor {
using UnderlyingT = typename T::value_type;
bool operator()(std::span<UnderlyingT> span, UnderlyingT needle) {
return std::find(span.begin(), span.end(), needle) != span.end();
}
};
template <typename Container, typename T>
bool Exist(Container &container, T needle) {
auto functor = ExistsFunctor<Container>();
return functor(container, needle);
}
int main() {
std::vector<int> arr{0, 1, 2, 3, 4};
bool ok = Exist(arr, 3);
if (ok) {
std::cout << "ok" << '\n';
} else {
std::cout << "not ok" << '\n';
}
return 0;
}
I want to design a print function for STL container, include: std::vector, std::map, std::unodered_map, std::set, std::unordered_set, std::list ....
The ideal function looks like:
template<typename T>
void Show(const T& t)
{
if (istype(t, std::vector))
{
// here is a presudo code
// print vector
for (auto i: t) cout << i << endl;
}
else if (istype(t, std::unordered_map))
{
// print unordered_map
}
else
{ }
}
I think the problem happens on istype()
I know there is some function like std::is_same can do this.
But it seems it just can operate on int or float, cant be operated on std::vector, can you help on this?
But it seems it just can operate on int or float, cant be operated
on std::vector, can you help on this?
For template classes like stranded containers, you need to specify the template argument to get the concrete type and thereafter you can compare usingstd::is_same. That means something like std::is_same_v<T, std::vector<int>> or std::is_same_v<T, std::vector<float>>, etc... will work.
On the other side, you need to see whether the passed container is a specialization for the standard container. There you need your own std::is_same_v like type traits.
One possible implementation will look like as follows. Also note that you need to use the if constexpr (since c++17) instead of normal if for compile time branching. If no access to c++17, you need to use SFINAE.
(See a Demo)
#include <iostream>
#include <type_traits> // std::false_type, std::true_type
#include <vector>
#include <list>
#include <string>
#include <map>
template<typename Type, template<typename...> class Args>
struct is_specialization final : std::false_type {};
template<template<typename...> class Type, typename... Args>
struct is_specialization<Type<Args...>, Type> : std::true_type {};
template<typename ContainerType>
constexpr void show(const ContainerType& container) noexcept
{
if constexpr (is_specialization<ContainerType, std::vector>::value
|| is_specialization<ContainerType, std::list>::value)
{
for (const auto ele : container)
std::cout << ele << '\t';
std::cout << '\n';
}
else if constexpr (is_specialization<ContainerType, std::map>::value)
{
for (const auto& [key, value]: container)
std::cout << key << " " << value << '\t';
std::cout << '\n';
}
// ... so on!
}
int main()
{
std::vector<int> vec{ 1, 2, 3 };
show(vec);
std::list<int> list{ 11, 22, 33 };
show(list);
std::map<int, std::string> mp{ {1, "string1"}, {2, "string2"}, {3, "string3"} };
show(mp);
}
Common feature of all STL containers is that they are iterable in range [begin(),end()). For sequence containers you have to handle printing T as value_type, for (Unordered) associative containers printing std::pair<const Key, T> as value_type must be handled. That is all. I don't see any reasons to checking type of passed container in your implemention.
template<class T>
void Print(const T& val) {
std::cout << val;
}
template<class Key, class Value>
void Print(const std::pair<const Key,Value>& p) {
Print(p.first);
std::cout << " - ";
Print(p.second);
}
template<class Cont>
void PrintCont(const Cont& cont) {
std::cout << std::endl;
for (auto it = cont.begin(); it != cont.end(); ++it) {
Print(*it);
std::cout << " ";
}
std::cout << std::endl;
}
std::array<int,2> a{1,2};
std::vector<int> v{1,2,3};
std::list<double> l{2., 3., 5.4};
std::map<int,double> m{ {1,2.},{10,3.14} };
std::multimap<int,int> m2{ {2,3},{4,5} };
std::set<float> s{1.f, 23.f, 2.f};
std::unordered_map<int,Foo> m3{ {1,Foo(1)}, {2,Foo(2)}, {3,Foo(3)}};
PrintCont(a);
PrintCont(v);
PrintCont(l);
PrintCont(m);
PrintCont(a);
PrintCont(s);
PrintCont(m2);
PrintCont(m3);
Live demo
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;
}
Consider the following declaration:
template <class T>
bool DoSomething(const T& value);
template <class D>
bool DoSomething(const std::vector<D>& value);
Is it possible to somehow unite this into single function declaration?
E.g. something like that:
template <class T, class D> bool DoSomething(...);
You code
template <class T>
bool DoSomething(const T& value);
is already accepting std::vector. If you want to do something in your DoSomething method which is different if T is a vector, then you can use this approach to check if the T is a specific type. Don't forget that templates are code generators.
Template parameters can be types, non-types, and templates.
And I suppose you are looking at something like this
#include <iostream>
#include <list>
#include <vector>
#include <string>
template <typename T, template <typename, typename> class Cont >
class Matrix{
public:
explicit Matrix(std::initializer_list<T> inList): data(inList){
for (auto d: data) std::cout << d << " ";
}
int getSize() const{
return data.size();
}
private:
Cont<T, std::allocator<T>> data;
};
int main(){
std::cout << std::endl;
Matrix<int, std::vector> myIntVec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
std::cout << std::endl;
std::cout << "myIntVec.getSize(): " << myIntVec.getSize() << std::endl;
Matrix<std::string, std::list> myStringList{"one", "two", "three", "four"};
std::cout << std::endl;
std::cout << "myStringList.getSize(): " << myStringList.getSize() << std::endl;
std::cout << std::endl;
}
Matrix is a simple class template, that can be initialised by a std::initializer_list . A Matrix can be used with a std::vector , or a std::list to hold its values.
live Demo
Well, template <class T> bool DoSomething(const T& value); can also be calles with any vector. If it works depends on what you are trying to do. Templates are just code generators. So whatever class T is, it can be used as template paramter if it has all the required members.
For example the following would work:
#include <iostream>
#include <vector>
#include <string>
class MyContainer {
public:
size_t size() const
{
return 2;
}
int front() const
{
return 0;
}
int back() const
{
return 1;
}
};
template<class T>
void foo(const T& t)
{
if (t.size() >= 2)
{
std::cout << "(" << t.front() << ", " << t.back() << ")" << std::endl;
}
}
int main()
{
std::vector<std::string> stringVec{ "abc", "def" };
MyContainer cont;
foo(stringVec); // prints "(abc, def)"
foo(cont); // prints "(0, 1)"
}
That's because both MyContainer and std::vector<std::string> have all the methods that are uses in the template. Actually this code should work with almost all STL-Containers.
Short answer: Geneally you can't.
Honest answer: Yes, it can be done, depends on what you plan to do with argument. In abstract case you would need to generalize the function prototype, but still have two specializations of template using SFINAE, which makes three declarations instead of two.
Long answer: In some cases you can take advantage of if constexpr
#include <iostream>
#include <type_traits>
#include <vector>
template<typename Test, template<typename...> class Ref>
struct is_specialization : std::false_type {};
template<template<typename...> class Ref, typename... Args>
struct is_specialization<Ref<Args...>, Ref>: std::true_type {};
template <class T> bool DoSomething(const T& arg)
{
if constexpr(is_specialization<T, std::vector>::value)
{
return !arg.empty();
} else
{
return bool(arg);
}
}
int main()
{
std::vector<int> s;
std::cout << DoSomething(s) << std::endl;
std::cout << DoSomething(1) << std::endl;
}
I'd like to write a class test that is able to store a function that is able to iterate through a collection of elements identified by the classic [first,last) iterator pair, that is:
template <typename T>
struct sum
{
template <typename I>
T operator()(I first, I last) const
{
T res = 0;
while (first != last)
{
res += *first;
++first;
}
return res;
}
};
//...
int main()
{
test<double> t;
t.set(sum<double>);
double a[] {1.,2.,3.};
std::cout << "Test (array) => " << t.call(a, a+3) << std::endl;
std::vector<double> v {1.,2.,3.};
std::cout << "Test (vector) => " << t.call(v.begin(), v.end()) << std::endl;
std::list<double> l {1.,2.,3.};
std::cout << "Test (list) => " << t.call(l.begin(), l.end()) << std::endl;
}
I thought to use std::function, but I've failed to do this as I wasn't able to declare the templated iterator pair.
A possible workaround is the following, which however only works with plain arrays (e.g., double[] or double*, like the above variable a), but not with other containers (e.g., like the above variables v and l):
template <typename T>
class test
{
public:
template <typename F>
void set(F f)
{
f_ = f;
}
template <typename I>
T call(I first, I last) const
{
return f_(first, last);
}
private:
std::function<T(T*,T*)> f_;
};
Any idea on how can I get the correct behavior?
NOTE: I'm compiling with GCC 4.9.2 --std=c++11
Thank you very much.
What you want is really to be able to construct a:
std::function<T(FwdIter<T>, FwdIter<T>)>
where FwdIter<T> is some type-erased class that satsifes the ForwardIterator concept and is dereferenced to a T. For that, check out the Boost.TypeErasure library, where we can do:
#include <boost/type_erasure/any.hpp>
#include <boost/type_erasure/operators.hpp>
#include <boost/mpl/vector.hpp>
using namespace boost::type_erasure;
template <typename T>
using FwdIter = any<
boost::mpl::vector<
copy_constructible<>,
incrementable<>,
dereferenceable<T>,
equality_comparable<>
>>;
With that and your definition of sum, I can do:
std::function<int(FwdIter<int>, FwdIter<int>)> f = sum<int>{};
std::vector<int> v = {1, 2, 3, 4, 5};
std::cout << f(v.begin(), v.end()) << std::endl; // prints 15
In your test<T>, you could just have a std::function<T(FwdIter<T>, FwdIter<T>)> member as desired.
I've tried to work on an alternative solution.
Essentially, the user function is wrapped inside a holder holder which fix the function signature to T(const std::vector<T>&). With respect to #Barry's solution (the one I've accepted), this doesn't require external libraries. However it suffers of performance issues due to the construction of the vector object at runtime. Also, and more importantly, as pointed out by #Barry, this solution imposes artificial requirements on T (like T must be copyable).
Here is it:
template <typename T,typename F>
class holder
{
public:
holder(F f) : f_(f) { }
T operator()(const std::vector<T>& v) const
{
return f_(v.begin(), v.end());
}
private:
F f_;
};
template <typename T>
class test_v2
{
public:
template <typename F>
void set(F f)
{
f_ = holder<T,F>(f);
}
template <typename I>
T call(I first, I last) const
{
return f_(std::vector<T>(first, last));
}
private:
std::function<T(const std::vector<T>&)> f_;
};
Why not stoke the functor instead, something like:
template <typename T>
class test
{
public:
template <typename I>
auto call(I first, I last) const
-> decltype(T()(first, last))
{
return T()(first, last);
}
};
And use it:
test<sum<double>> t;
Live example