How to capture variable inside lambda - c++

I have code from here:
std::sort(begin(v), end(v), [](auto const &t1, auto const &t2) {
return get<0>(t1) < get<0>(t2); // or use a custom compare function
});
I wanted to sort tuple multiple times so I wrote this code:
int k = 10;
while(k--){
std::sort(begin(v), end(v), [](auto const &t1, auto const &t2) {
return get<k>(t1) < get<k>(t2); // or use a custom compare function
});
}
but I get error error: ‘k’ is not captured. I tried to do it in this way:
int k = 10;
while(k--){
std::sort(begin(v), end(v), [&k](auto const &t1, auto const &t2) {
return get<k>(t1) < get<k>(t2); // or use a custom compare function
});
}
but it is not the proper way and error error: the value of ‘k’ is not usable in a constant expression occurs.
How to capture k variable?

As mch said in the comment, the problem is that k is not a compile time constant.
For a compile time constant, to iterate from N to 0, you may need template and recursion:
#include <algorithm>
#include <tuple>
#include <type_traits>
#include <vector>
using namespace std; // just for simplify, and not recommended in practice
template <size_t N, typename Iterator, enable_if_t<N == 0, int> = 0>
void foo(Iterator begin, Iterator end)
{
sort(begin, end,
[](const auto &t1, const auto &t2) {
return get<0>(t1) < get<0>(t2);
}
);
}
template <size_t N, typename Iterator, enable_if_t<N != 0, int> = 0>
void foo(Iterator begin, Iterator end)
{
sort(begin, end,
[](const auto &t1, const auto &t2) {
return get<N>(t1) < get<N>(t2);
}
);
foo<N - 1>(begin, end);
}
int main()
{
vector<tuple<int, int>> v{{0, 1}, {0, 0}, {1, 1}};
foo<1>(v.begin(), v.end());
// posible results:
// {0, 0}, {0, 1}, {1, 1}
// {0, 1}, {0, 0}, {1, 1} // impossible if use std::stable_sort instead
}

std::get only accepts a template argument which is an expression whose value that can be evaluated at compiling time.
You can't use k, because it is a variable that changes value.
std::sort(begin(v), end(v), [](auto const &t1, auto const &t2) {
const int k = 3;
return std::get<k>(t1) < std::get<k>(t2); // or use a custom compare function
});
As I wrote in the comments, I know const int = 3 will shadow the k value outside of the lambda expression, but this example shows that get will work when it it receives a compiling time constant value.
For example, if you try to set k = 5, for example where v only has 4 tuple parameters, the compiler will give an error because it knows that this is out of range.
The following code will give an error, but if k is set to 3, it will work
std::vector<std::tuple<int, int, int, int>> v;
std::sort(begin(v), end(v), [](auto const &t1, auto const &t2) {
const int k = 5;
return std::get<k>(t1) < std::get<k>(t2); // or use a custom compare function
});

#include <tuple>
#include <utility>
#include <cstddef>
#include <algorithm>
#include <cstddef>
#include <iterator>
template <std::size_t I, typename It, typename F>
void sort_tuple(It it, It end, F f)
{
std::stable_sort(it, end, [f](const auto& t1, const auto& t2)
{
return f(std::get<I>(t1), std::get<I>(t2));
});
}
template <typename It, typename F, std::size_t... Is>
void sort_tuple(It it, It end, F f, std::index_sequence<Is...>)
{
int dummy[] = { 0, (sort_tuple<sizeof...(Is) - Is - 1>(it, end, f), 0)... };
static_cast<void>(dummy);
}
template <typename It, typename F>
void sort_tuple(It it, It end, F f)
{
sort_tuple(it, end, f, std::make_index_sequence<
std::tuple_size<typename std::iterator_traits<It>::value_type>::value
>{});
}
Test:
std::vector<std::tuple<int, int, int>> v{ {2,1,2}, {2,2,2}, {3,2,1}
, {1,1,1}, {1,2,1}, {2,2,1} };
sort_tuple(begin(v), end(v), [](const auto& t1, const auto& t2)
{
return t1 < t2;
});
for (auto& t : v)
{
std::cout << std::get<0>(t) << " " << std::get<1>(t) << " " << std::get<2>(t) << std::endl;
}
DEMO

You are using k as a template argument so it must be available at compile time. One way to do this by making it a constexpr:
constexpr int k = 1;
int j = k;
while(j--){
std::sort(begin(v), end(v), [&k](auto const &t1, auto const &t2) {
return get<k>(t1) < get<k>(t2); // or use a custom compare function
})
}

Template non-type arguments must be compile time constants. int k=0; is not a compile time constant.
template<std::size_t...Is>
auto index_over(std::index_sequence<Is...> ={}){
return [](auto&&f)->decltype(auto){
return f( std::integal_constant<std::size_t,Is>{}... );
};
}
template<std::size_t N>
auto index_upto(std::integral_constant<N> ={}){
return index_over(std::make_index_sequence<N>{});
}
these are helpers to get efficient compile time values from 0 up to N-1.
auto foreacher=[](auto&&f){
return [f](auto&&...args){
using discard=int[];
(void)discard{0,(void(
f(decltype(args)(args))
),0)...};
};
};
the above is obscure c++14 to replace short c++17 code. foreacher(f) returns a function g. g(a,b,c) does f(a) then f(b) then f(c).
Now glue it together:
auto sort_v_by_k=[&](auto k){
std::sort(begin(v), end(v), [](auto const &t1, auto const &t2) {
return get<k>(t1) < get<k>(t2); // or use a custom compare function
});
};
index_upto<11>()( foreacher( [&](auto k){
sort_v_by_k( std::integral_constant<std::size_t, 11-k >{} );
}));
and ignoring typos, done.

Related

Create constexpr template function to sum any container in C++

I need to create a constexpr template function to sum container, and pass the static assert. This is my code, but I have to questions.
I recived this error: assignment of read-only reference ‘res’.
Is the sum function written correctly?
#include <iostream>
#include <array>
using namespace std;
template <typename T>
constexpr auto Sum(const T& arr)
{
using arrType = decltype(*arr.begin());
arrType res = 0;
for (auto it = arr.begin(); it != arr.end(); it++) {
res += *it;
}
return res;
}
int main()
{
constexpr array<int, 3> base{5, 2, 0};
static_assert(Sum(base) == 7);
return 0;
}
Your code doesn't compile because since arr is const reference, decltype(*arr.begin()) is const int. So your res is const and you can't assign to it in your loop.
To fix only that error, you should use std::decay, i.e:
using arrType = std::decay_t<decltype(*arr.begin())>;
You don't specify what you mean by "container". If you mean the standard library's definition (which is supported by all STL containers), you can use value_type instead and also further simplify your code:
template <typename T>
constexpr auto Sum(const T& arr) {
typename T::value_type result{};
for (auto const& x : arr) {
result += x;
}
return result;
}
If you have C++20, you can also use std::accumulate:
template <typename T>
constexpr auto Sum(const T& arr)
{
return std::accumulate(arr.begin(), arr.end(),
typename T::value_type{});
}
The problem lies with decltype(*arr.begin()), which gives const U& if U represents the type of the elements of the container arr.
Applying std::remove_reference and std::remove_cv makes it compile.
#include <iostream>
#include <type_traits>
#include <array>
using namespace std;
template <typename T>
constexpr auto Sum(const T& arr)
{
using arrType = remove_cv_t<remove_reference_t<decltype(*arr.begin())>>;
arrType res = 0;
for (auto it = arr.begin(); it != arr.end(); it++) {
res += *it;
}
return res;
}
int main()
{
constexpr array<int, 3> base{5, 2, 0};
static_assert(Sum(base) == 7);
return 0;
}

Conversion error when returning an Iterator

I'm experimenting with the "find" function. I can't seem to be able to return
the iterator.
template<typename T>
typename T::iterator do_find(const T &v, int f)
{
return find(v.begin(), v.end(), f);
}
And here is my main :
int main()
{
std::vector<int> v = {1, 2, 3, 3, 4, 6};
std::vector<int>::iterator it;
it = do_find(v, 3);
return 0;
}
When I compile I get the following error :
error: impossible de convertir
« std::find<__gnu_cxx::__normal_iterator<const int*, std::vector<int> >, int>((& v)->std::vector<int>::begin(), (& v)->std::vector<int>::end(), f) » de « __gnu_cxx::__normal_iterator<const int*, std::vector<int> > » vers « std::vector<int>::iterator {aka __gnu_cxx::__normal_iterator<int*, std::vector<int> >} »
v is declared as const, then for std::vector, both v.begin() and v.end() return std::vector::const_iterator, then the return type of find will be std::vector::const_iterator too; it can't be converted to std::vector::iterator implicitly.
You can change the return type, e.g.
template<typename T>
typename T::const_iterator do_find(const T &v, int f)
{
return find(v.begin(), v.end(), f);
}
or just
template<typename T>
auto do_find(const T &v, int f)
{
return find(v.begin(), v.end(), f);
}
then
auto it = do_find(v, 3);
If you want to modify the element through the returned iterator, then you should declare the parameter v to be non-const.
template<typename T>
auto do_find(T &v, int f)
{
return find(v.begin(), v.end(), f);
}
Note that with auto, the above do_find will return iterator if you pass a non-const vector, and return const_iterator if you pass a const vector.
v is const; meaning that std::find will return a T::const_iterator. You're attempting to return a T::iterator; and the compiler can't convert from const to non-const.
The fix is to either return the const_iterator or to make v non-const. Depending on exactly what you want to do with the iterator.
Within the function the container is declared as a constant container
template <typename T>
typename T::iterator do_find(const T &v, int f);
^^^^^^^^^^
So the member functions begin and end of this container return objects of the type typename T::const_iterator that can not be implicitly converted to the type.typename T::iterator.
Also it is not clear why the second parameter has the type int instead of the type typename T::value_type.
There is a magic word auto in C++ that can simplify the function declaration and using its return value.
The function can be defined the following way
template <typename T>
auto do_find( const T &v, typename T::value_type value ){
return std::find( v.begin(), v.end(), value );
}
Here is a demonstrative program
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
template <typename T>
auto do_find( const T &v, typename T::value_type value ){
return std::find( v.begin(), v.end(), value );
}
int main()
{
std::vector<int> v = { 1, 2, 3, 3, 4, 6 };
auto it = do_find(v, 3);
if ( it != v.end() )
{
std::cout << *it << " is found at position "
<< std::distance( v.cbegin(), it ) << std::endl;
}
return 0;
}
Its output is
3 is found at position 2

Custom range for boost::range library

I’m writing filter and map algorithms using boost::range library:
template <class Range> struct Converter
{
Converter(const Range& p_range) : m_range(p_range) {}
template<class OutContainer> operator OutContainer() const
{
return {m_range.begin(), m_range.end()};
}
private:
Range m_range;
};
template<class Range> Converter<Range> convert(const Range& p_range) { return {p_range}; }
template<class Range, class Fun> auto map(Range&& p_range, Fun&& p_fun)
{
return convert(p_range | boost::adaptors::transformed(p_fun));
}
template<class Range, class Pred> auto filter(Range&& p_range, Pred&& p_pred)
{
return convert(p_range | boost::adaptors::filtered(p_pred));
}
Right now I can use them like this:
std::vector<int> l_in = {1, 2, 3, 4, 5};
std::vector<int> l_tmp_out = filter(l_in, [](int p){ return p < 4; });
std::vector<int> l_out = map(l_tmp_out, [](int p){ return p + 5; });
I would also like to write code this way:
map(filter(l_in, [](int p){ return p < 4; }), [](int p){ return p + 5; });
Unfortunately my Converter class does not compose with boost::range algorithms so this example does not compile. I'm looking for a proper way to change that.
UPDATE
I followed #sehe link and it turned out that all I had to do was to add this four lines to Converter class:
using iterator = typename Range::iterator;
using const_iterator = typename Range::const_iterator;
auto begin() const { return m_range.begin(); }
auto end() const { return m_range.end(); }
Here's my take on things:
Live On Coliru
#include <boost/range.hpp>
#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm.hpp>
#include <iostream>
#include <vector>
namespace MyRange {
template <typename R> struct Proxy {
Proxy(R&& r) : _r(std::move(r)) {}
Proxy(R const& r) : _r(r) {}
template <typename OutContainer> operator OutContainer() const {
return boost::copy_range<OutContainer>(_r);
}
using iterator = typename boost::range_mutable_iterator<R>::type;
using const_iterator = typename boost::range_const_iterator<R>::type;
auto begin() const { return range_begin(_r); }
auto end() const { return range_end(_r); }
auto begin() { return range_begin(_r); }
auto end() { return range_end(_r); }
private:
R _r;
};
template <typename R> auto make_proxy(R&& r) { return Proxy<R>(std::forward<R>(r)); }
template <typename Range, typename Fun> auto map(Range&& p_range, Fun&& p_fun) {
return make_proxy(std::forward<Range>(p_range) | boost::adaptors::transformed(std::forward<Fun>(p_fun)));
}
template <typename Range, typename Pred> auto filter(Range&& p_range, Pred&& p_pred) {
return make_proxy(std::forward<Range>(p_range) | boost::adaptors::filtered(std::forward<Pred>(p_pred)));
}
}
int main() {
using namespace MyRange;
{
std::vector<int> l_in = {1, 2, 3, 4, 5};
std::vector<int> l_tmp_out = filter(l_in, [](int p){ return p < 4; });
std::vector<int> l_out = map(l_tmp_out, [](int p){ return p + 5; });
boost::copy(l_out, std::ostream_iterator<int>(std::cout << "\nfirst:\t", "; "));
}
{
boost::copy(
map(
filter(
std::vector<int> { 1,2,3,4,5 },
[](int p){ return p < 4; }),
[](int p){ return p + 5; }),
std::ostream_iterator<int>(std::cout << "\nsecond:\t", "; "));
}
}
Prints
first: 6; 7; 8;
second: 6; 7; 8;
NOTES
it uses std::forward<> more accurately
it uses const/non-const iterators
it uses Boost Range traits (range_mutable_iterator<> etc.) instead of hardcoding assuming nested typedefs. This allows things to work with other ranges (e.g. std::array<> or even int (&)[]).
the user-defined converson operator uses boost::copy_range<> for similar reasons

Overloading std::transform algorithm

Consider this standard use of std::transform algorithm:
vector<int> in = { 1, 2, 3 };
auto f0 = [](int val) { return val + 1; };
auto f1 = [](int val) { return val > 1; };
vector<int> out0(in.size());
std::transform(in.begin(), in.end(), out0.begin(), f0);
vector<bool> out1(in.size());
std::transform(in.begin(), in.end(), out1.begin(), f1);
This works fine but is long to write. I would like to write something like this:
auto out0 = tranform(in, f0);
auto out1 = tranform(in, f1);
How to overload the transform algorithm to allow this syntax?
The following should do what you want
#include <algorithm>
#include <iostream>
#include <type_traits>
#include <vector>
template<typename T, typename F>
std::vector<typename std::result_of<F(T)>::type>
transform(const std::vector<T>& l, F func)
{
typedef typename std::result_of<F(T)>::type FunctorReturnType;
std::vector<FunctorReturnType> out(l.size());
std::transform(l.begin(), l.end(), out.begin(), func);
return out;
}
int main ()
{
const std::vector<int> in{ 1, 2, 3 };
auto f0 = [](int val) { return val + 1; };
auto f1 = [](int val) { return val > 1; };
auto out0 = transform(in, f0);
auto out1 = transform(in, f1);
for (const auto& m: out0) std::cout << m << std::endl;
for (const auto& m: out1) std::cout << m << std::endl;
}
Do you like template template arguments? This works with more containers than vectors.
template<template<class...> class C, class F, class T, class... Tail>
C<typename result_of<F(T)>::type>
transform(const C<T, Tail...>& in, F func)
{
C<typename result_of<F(T)>::type> out(in.size());
std::transform(in.begin(), in.end(), out.begin(), func);
return out;
}
Can be seen working at http://coliru.stacked-crooked.com/a/767adb662d7cbe42
EDIT: changed to not use the source's allocator in the resulting container.
Why aren't you ok with the following, which doesn't use any C++11 magic such as decltype ?
#include <algorithm>
#include <vector>
template<class T, class F>
std::vector<T> transform(const std::vector<T>& l, F func)
{
std::vector<T> out(l.size());
std::transform(l.begin(), l.end(), out.begin(), func);
return out;
}

reduce arguments

My question is that I have two arguments vector<int>& ivec and const int& numb. is there a way to eliminate const int& numb? I just want to do add 10 to all elements in the vector. Also I want to eliminate if part, since it's empty. Thanks in advance. Please do it recursively. My goal it to use as few arguments as possible in a recursive function.
#include <iostream>
#include <vector>
using namespace std;
vector<int> addten(vector<int>& ivec, const int& numb) {
if (numb == 0) {
} else {
ivec[numb - 1] += 10;
addten(ivec, numb - 1);
}
return ivec;
}
Your code feels very odd. The C++y way to do this would be:
std::vector<int> vec; // your vector
// adds 10 in place
std::transform(vec.begin(), vec.end(), vec.begin(),
std::bind(std::plus<int>(), _1, 10));
// adds 10 out-of-place
std::vector<int> result;
std::transform(vec.begin(), vec.end(), std::back_inserter(result),
std::bind(std::plus<int>(), _1, 10));
As you have specifically requested, I've implemented a very poor foldl in C++ that operates only on vector<T> instead of on iterators.
#include <vector>
#include <iostream>
// well, here comes C++ origami
template<typename Start, typename F, typename T>
Start foldl(Start s, F f, const std::vector<T>& v) {
return foldl_impl(s, f, v, 0);
}
template<typename Start, typename F, typename T>
Start foldl_impl(Start s, F f, const std::vector<T>& v,
typename std::vector<T>::size_type t) {
if(t == v.size()) return s;
typename std::vector<T>::size_type t2 = t++;
return foldl_impl(f(s, v[t2]), f, v, t);
}
int main()
{
std::vector<int> vec = {1, 2, 3, 4, 5, 6, 7};
std::vector<int> added =
foldl(std::vector<int>()
, [](std::vector<int>& v, int i) { v.push_back(i+10); return v;}
, vec);
for(auto x : added) {
std::cout << x << std::endl;
}
return 0;
}
Please consider that this is far from good C++ style.