Convert tuple to pair - c++

Have educational task: write template function, which takes arbitrary std::tuple and 2 indexes inside and returns std::pair, containing elements of given std::tuple with correspondent indexes.
Example:
auto t = std::make_tuple(0, 3.5, "Hello");
std::pair<double, char const *> p = to_pair<1,2>(t);
// p contains 3.5 and "Hello"
Written something like this:
template<int I, int J>
auto to_pair(std::tuple t) -> decltype(std::make_pair(std::get<I>(t), std::get<J>(t))) {
return std::make_pair(std::get<I>(t), std::get<J>(t));
}
However got an error:
r: missing template arguments before ‘t’
auto to_pair(std::tuple t) -> decltype(std::make_pair(get<I>t, get<J>t))
^
What I'm doing wrong and what is correct syntax here?

std::tuple is a template class, so there's no std::tuple, only std::tuple<T, ...>. In your case, the type of t is std::tuple<int, double, char const *>.
Also, you're calling std::get without an argument (there's missing braces).
You're almost there, the function should be something along the lines of:
template<int I, int J, class... T>
auto to_pair(std::tuple<T...> t)
-> decltype(std::make_pair(std::get<I>(t), std::get<J>(t))) {
return std::make_pair(std::get<I>(t), std::get<J>(t));
}

Related

Convert element in std::tuple

I have a std::tuple with strings in it and I want to convert an element of that tuple to an Integer. Is that possible?
E.g:
std::tuple<std::string,std::string>("1","2") --> std::tuple<int,std::string>(1,"2")
If you want to change one of the underlying types of the std::tuple container during runtime then no, it is not possible. If you simply want to convert one of the values to int then you should define another tuple and utilize the std::stoi function:
std::tuple<std::string, std::string> t1("1", "2");
std::tuple<int, std::string> t2(std::stoi(std::get<0>(t1)), std::get<1>(t1));
or use the std::make_tuple function and the auto specifier and let the compiler deduce the types:
auto t3 = std::make_tuple(std::stoi(std::get<0>(t1)), std::get<1>(t1));
Here is a generic solution:
template <std::size_t At, class NewType, class Tuple, std::size_t... Idx>
auto convertAt_impl(Tuple const &orig, std::index_sequence<Idx...>) {
return std::tuple{
[&] {
if constexpr(Idx == At)
return boost::lexical_cast<NewType>(std::get<Idx>(orig));
else
return std::get<Idx>(orig);
}()...
};
}
template <std::size_t At, class NewType, class... Ts>
auto convertAt(std::tuple<Ts...> const &orig) {
return convertAt_impl<At, NewType>(orig, std::index_sequence_for<Ts...>{});
}
It makes use of boost::lexical_cast to perform the conversion, but you can replace it with whatever conversion facility you want.
It can be invoked like so:
int main() {
std::tuple tup{"1", "2"};
auto tup2 = convertAt<0, int>(tup);
static_assert(std::is_same_v<decltype(tup2), std::tuple<int, char const *>>);
std::cout << std::get<0>(tup2) << ' ' << std::get<1>(tup2) << '\n';
}
... which will pass the assert and print what we expect.
I would include a link to a Coliru demo, but its GCC catches fire upon pack-expanding a lambda, while its Clang has no idea of what if constexpr or class template argument deduction are. Heh.

C++ template type deduction fails for lambda to function pointer conversion [duplicate]

I'm learning C++, and I'm trying to implement a binary search function that finds the first element for which a predicate holds. The function's first argument is a vector and the second argument is a function that evaluates the predicate for a given element. The binary search function looks like this:
template <typename T> int binsearch(const std::vector<T> &ts, bool (*predicate)(T)) {
...
}
This works as expected if used like this:
bool gte(int x) {
return x >= 5;
}
int main(int argc, char** argv) {
std::vector<int> a = {1, 2, 3};
binsearch(a, gte);
return 0;
}
But if I use a lambda function as a predicate, I get a compiler error:
search-for-a-range.cpp:20:5: error: no matching function for call to 'binsearch'
binsearch(a, [](int e) -> bool { return e >= 5; });
^~~~~~~~~
search-for-a-range.cpp:6:27: note: candidate template ignored: could not match 'bool (*)(T)' against '(lambda at
search-for-a-range.cpp:20:18)'
template <typename T> int binsearch(const std::vector<T> &ts,
^
1 error generated.
The above error is generated by
binsearch(a, [](int e) -> bool { return e >= 5; });
What's wrong? Why is the compiler not convinced that my lambda has the right type?
Your function binsearch takes a function pointer as argument. A lambda and a function pointer are different types: a lambda may be considered as an instance of a struct implementing operator().
Note that stateless lambdas (lambdas that don't capture any variable) are implicitly convertible to function pointer. Here the implicit conversion doesn't work because of template substitution:
#include <iostream>
template <typename T>
void call_predicate(const T& v, void (*predicate)(T)) {
std::cout << "template" << std::endl;
predicate(v);
}
void call_predicate(const int& v, void (*predicate)(int)) {
std::cout << "overload" << std::endl;
predicate(v);
}
void foo(double v) {
std::cout << v << std::endl;
}
int main() {
// compiles and calls template function
call_predicate(42.0, foo);
// compiles and calls overload with implicit conversion
call_predicate(42, [](int v){std::cout << v << std::endl;});
// doesn't compile because template substitution fails
//call_predicate(42.0, [](double v){std::cout << v << std::endl;});
// compiles and calls template function through explicit instantiation
call_predicate<double>(42.0, [](double v){std::cout << v << std::endl;});
}
You should make your function binsearch more generic, something like:
template <typename T, typename Predicate>
T binsearch(const std::vector<T> &ts, Predicate p) {
// usage
for(auto& t : ts)
{
if(p(t)) return t;
}
// default value if p always returned false
return T{};
}
Take inspiration from standard algorithms library.
The lambda expression with empty capture list could be implicitly converted to a function pointer. But the function pointer predicate is taking T as its parameter, that need to be deduced. Type conversion won't be considered in template type deduction, T can't be deduced; just as the error message said, candidate template (i.e. binsearch) is ignored.
You can use operator+ to achieve this, it'll convert lambda to function pointer, which will be passed to binsearch later and then T will be successfully deduced[1].
binsearch(a, +[](int e) -> bool { return e >= 5; });
// ~
Of course you could use static_cast explicitly:
binsearch(a, static_cast<bool(*)(int)>([](int e) -> bool { return e >= 5; }));
Note if you change predicate's type to be independent of T, i.e. bool (*predicate)(int), passing lambda with empty capture list would work too; the lambda expression will be converted to function pointer implicitly.
Another solution is changing the parameter type from function pointer to std::function, which is more general for functors:
template <typename T> int binsearch(const std::vector<T> &ts, std::function<bool (typename std::vector<T>::value_type)> predicate) {
...
}
then
binsearch(a, [](int e) -> bool { return e >= 5; });
[1] A positive lambda: '+[]{}' - What sorcery is this?
Why is the compiler not convinced that my lambda has the right type?
Template functions being told to deduce their template parameters do no conversion. A lambda is not a function pointer, so there is no way to deduce the T in that argument. As all function arguments independently deduce their template parameters (unless deduction is blocked), this results in an error.
There are a number of fixes you can do.
You can fix the template function.
template <class T>
int binsearch(const std::vector<T> &ts, bool (*predicate)(T))
Replace the function pointer with a Predicate predicate or Predicate&& predicate and leave the body unchanged.
template <class T, class Predicate>
int binsearch(const std::vector<T> &ts, Predicate&& predicate)
Use deduction blocking:
template<class T>struct tag_t{using type=T;};
template<class T>using block_deduction=typename tag_t<T>::type;
template <class T>
int binsearch(const std::vector<T> &ts, block_deduction<bool (*)(T)> predicate)
optionally while replacing the function pointer with a std::function<bool(T)>.
You can fix it at the call site.
You can pass T manually binsearch<T>(vec, [](int x){return x<0;}).
You can decay the lambda to a function pointer with putting a + in front of it +[](int x) ... or a static_cast<bool(*)(int)>( ... ).
The best option is Predicate one. This is also what standard library code does.
We can also go a step further and make your code even more generic:
template <class Range, class Predicate>
auto binsearch(const Range &ts, Predicate&& predicate)
-> typename std::decay< decltype(*std::begin(ts)) >::type
The -> typename std::decay ... trailing return type part can be eliminated in C++14.
A benefit to this is that if the body also uses std::begin and std::end to find begin/end iterators, binsearch now supports deques, flat C-style arrays, std::arrays, std::strings, std::vectors, and even some custom types.
If you have any control over binsearch I suggest you refactor it:
template <typename T, typename Predicate>
int binsearch(std::vector<T> const& vec, Predicate&& pred) {
// This is just to illustrate how to call pred
for (auto const& el : vec) {
if (pred(el)) {
// Do something
}
}
return 0; // Or anything meaningful
}
Another way for you is to perform type-erasure on your functor objects/function pointers/whatever... by embedding them into an std::function<bool(T const&)>. To do so, just rewrite the function above as:
template <typename T>
int binsearch(std::vector<T> const& vec, std::function<bool(T const&)> pred);
But since template argument deduction does not do any conversion, you need to explicitly feed your function like the following:
auto my_predicate = [](int x) { return true; }; // Replace with actual predicate
std::vector<int> my_vector = {1, 2, 3, 4};
binsearch(my_vector, std::function<bool (int const&)>(my_predicate));
However given the description of your function, it seems it does the same job as std::find_if.
std::vector<int> my_vector = {1, 12, 15, 13, 16};
auto it = std::find_if(std::begin(my_vector), std::end(my_vector),
[](int vec_el) { return !vec_el%5; });
// it holds the first element in my_vector that is a multiple of 5.
if (it != std::end(my_vector)) {
std::cout << *it << std::endl; // prints 15 in this case
}
Note that to do a binary search, you need more than just a predicate: you need a predicate that defines an order on your range AND a target value.
A function pointer and a lambda function is not the same thing.
An object t can not be assigned to predicate where:
bool (*predicate)(int)
and
auto t = [](int e) -> bool { return e >= 5; });
Might as well use std::function<bool(int)>. Your signature will look like this:
template <typename T>
int binsearch(const std::vector<T> &ts, std::function<bool(T)> predicate){
// ...
}
Now that's not a function pointer, You'll need to bind your founction pointers if they are necessary, (I assume you're fine with just lambdas)

Passing a lambda into a function template

I'm learning C++, and I'm trying to implement a binary search function that finds the first element for which a predicate holds. The function's first argument is a vector and the second argument is a function that evaluates the predicate for a given element. The binary search function looks like this:
template <typename T> int binsearch(const std::vector<T> &ts, bool (*predicate)(T)) {
...
}
This works as expected if used like this:
bool gte(int x) {
return x >= 5;
}
int main(int argc, char** argv) {
std::vector<int> a = {1, 2, 3};
binsearch(a, gte);
return 0;
}
But if I use a lambda function as a predicate, I get a compiler error:
search-for-a-range.cpp:20:5: error: no matching function for call to 'binsearch'
binsearch(a, [](int e) -> bool { return e >= 5; });
^~~~~~~~~
search-for-a-range.cpp:6:27: note: candidate template ignored: could not match 'bool (*)(T)' against '(lambda at
search-for-a-range.cpp:20:18)'
template <typename T> int binsearch(const std::vector<T> &ts,
^
1 error generated.
The above error is generated by
binsearch(a, [](int e) -> bool { return e >= 5; });
What's wrong? Why is the compiler not convinced that my lambda has the right type?
Your function binsearch takes a function pointer as argument. A lambda and a function pointer are different types: a lambda may be considered as an instance of a struct implementing operator().
Note that stateless lambdas (lambdas that don't capture any variable) are implicitly convertible to function pointer. Here the implicit conversion doesn't work because of template substitution:
#include <iostream>
template <typename T>
void call_predicate(const T& v, void (*predicate)(T)) {
std::cout << "template" << std::endl;
predicate(v);
}
void call_predicate(const int& v, void (*predicate)(int)) {
std::cout << "overload" << std::endl;
predicate(v);
}
void foo(double v) {
std::cout << v << std::endl;
}
int main() {
// compiles and calls template function
call_predicate(42.0, foo);
// compiles and calls overload with implicit conversion
call_predicate(42, [](int v){std::cout << v << std::endl;});
// doesn't compile because template substitution fails
//call_predicate(42.0, [](double v){std::cout << v << std::endl;});
// compiles and calls template function through explicit instantiation
call_predicate<double>(42.0, [](double v){std::cout << v << std::endl;});
}
You should make your function binsearch more generic, something like:
template <typename T, typename Predicate>
T binsearch(const std::vector<T> &ts, Predicate p) {
// usage
for(auto& t : ts)
{
if(p(t)) return t;
}
// default value if p always returned false
return T{};
}
Take inspiration from standard algorithms library.
The lambda expression with empty capture list could be implicitly converted to a function pointer. But the function pointer predicate is taking T as its parameter, that need to be deduced. Type conversion won't be considered in template type deduction, T can't be deduced; just as the error message said, candidate template (i.e. binsearch) is ignored.
You can use operator+ to achieve this, it'll convert lambda to function pointer, which will be passed to binsearch later and then T will be successfully deduced[1].
binsearch(a, +[](int e) -> bool { return e >= 5; });
// ~
Of course you could use static_cast explicitly:
binsearch(a, static_cast<bool(*)(int)>([](int e) -> bool { return e >= 5; }));
Note if you change predicate's type to be independent of T, i.e. bool (*predicate)(int), passing lambda with empty capture list would work too; the lambda expression will be converted to function pointer implicitly.
Another solution is changing the parameter type from function pointer to std::function, which is more general for functors:
template <typename T> int binsearch(const std::vector<T> &ts, std::function<bool (typename std::vector<T>::value_type)> predicate) {
...
}
then
binsearch(a, [](int e) -> bool { return e >= 5; });
[1] A positive lambda: '+[]{}' - What sorcery is this?
Why is the compiler not convinced that my lambda has the right type?
Template functions being told to deduce their template parameters do no conversion. A lambda is not a function pointer, so there is no way to deduce the T in that argument. As all function arguments independently deduce their template parameters (unless deduction is blocked), this results in an error.
There are a number of fixes you can do.
You can fix the template function.
template <class T>
int binsearch(const std::vector<T> &ts, bool (*predicate)(T))
Replace the function pointer with a Predicate predicate or Predicate&& predicate and leave the body unchanged.
template <class T, class Predicate>
int binsearch(const std::vector<T> &ts, Predicate&& predicate)
Use deduction blocking:
template<class T>struct tag_t{using type=T;};
template<class T>using block_deduction=typename tag_t<T>::type;
template <class T>
int binsearch(const std::vector<T> &ts, block_deduction<bool (*)(T)> predicate)
optionally while replacing the function pointer with a std::function<bool(T)>.
You can fix it at the call site.
You can pass T manually binsearch<T>(vec, [](int x){return x<0;}).
You can decay the lambda to a function pointer with putting a + in front of it +[](int x) ... or a static_cast<bool(*)(int)>( ... ).
The best option is Predicate one. This is also what standard library code does.
We can also go a step further and make your code even more generic:
template <class Range, class Predicate>
auto binsearch(const Range &ts, Predicate&& predicate)
-> typename std::decay< decltype(*std::begin(ts)) >::type
The -> typename std::decay ... trailing return type part can be eliminated in C++14.
A benefit to this is that if the body also uses std::begin and std::end to find begin/end iterators, binsearch now supports deques, flat C-style arrays, std::arrays, std::strings, std::vectors, and even some custom types.
If you have any control over binsearch I suggest you refactor it:
template <typename T, typename Predicate>
int binsearch(std::vector<T> const& vec, Predicate&& pred) {
// This is just to illustrate how to call pred
for (auto const& el : vec) {
if (pred(el)) {
// Do something
}
}
return 0; // Or anything meaningful
}
Another way for you is to perform type-erasure on your functor objects/function pointers/whatever... by embedding them into an std::function<bool(T const&)>. To do so, just rewrite the function above as:
template <typename T>
int binsearch(std::vector<T> const& vec, std::function<bool(T const&)> pred);
But since template argument deduction does not do any conversion, you need to explicitly feed your function like the following:
auto my_predicate = [](int x) { return true; }; // Replace with actual predicate
std::vector<int> my_vector = {1, 2, 3, 4};
binsearch(my_vector, std::function<bool (int const&)>(my_predicate));
However given the description of your function, it seems it does the same job as std::find_if.
std::vector<int> my_vector = {1, 12, 15, 13, 16};
auto it = std::find_if(std::begin(my_vector), std::end(my_vector),
[](int vec_el) { return !vec_el%5; });
// it holds the first element in my_vector that is a multiple of 5.
if (it != std::end(my_vector)) {
std::cout << *it << std::endl; // prints 15 in this case
}
Note that to do a binary search, you need more than just a predicate: you need a predicate that defines an order on your range AND a target value.
A function pointer and a lambda function is not the same thing.
An object t can not be assigned to predicate where:
bool (*predicate)(int)
and
auto t = [](int e) -> bool { return e >= 5; });
Might as well use std::function<bool(int)>. Your signature will look like this:
template <typename T>
int binsearch(const std::vector<T> &ts, std::function<bool(T)> predicate){
// ...
}
Now that's not a function pointer, You'll need to bind your founction pointers if they are necessary, (I assume you're fine with just lambdas)

Higher-order functions in C++11

I'm trying to write a generic fold function using the new anonymous functions available in C++11, here is what I have:
template<typename T>
T foldl(std::function<T(T,T)> f, T initial, std::vector<T> items) {
T accum = initial;
for(typename std::vector<T>::iterator it = items.begin(); it != items.end(); ++it) {
accum = f(accum, (*it));
}
return accum;
}
The following attempt to use it:
std::vector<int> arr;
arr.assign(8, 2);
foldl([] (int x, int y) -> int { return x * y; }, 1, arr);
causes an error:
main.cpp:44:61: error: no matching function for call to 'foldl(main(int, char**)::<lambda(int, int)>, int, std::vector<int>&)'
main.cpp:44:61: note: candidate is:
main.cpp:20:3: note: template<class T> T foldl(std::function<T(T, T)>, T, std::vector<T>)
main.cpp:20:3: note: template argument deduction/substitution failed:
main.cpp:44:61: note: 'main(int, char**)::<lambda(int, int)>' is not derived from 'std::function<T(T, T)>'
It seems to me that using std::function is not the right way to go about defining the type of f. How can I correct this?
Your code is not very generic. There is no need to require a function, vector, or anything of the kind. And generally, in C++, functions would go at the end of the argument list (especially important for lambdas, as they can be big).
So it would be better (ie: more standard) to write this as this:
template<typename Range, typename Accum>
typename Range::value_type foldl(const Range &items, const typename Range::value_type &initial, Accum f)
{
typename Range::value_type accum = initial;
for(const auto &val : items) {
accum = f(accum, val);
}
return accum;
}
Or you could just use std::accumulate which does the exact same thing.
I'm not certain why this template is failing, but switching over to using a template parameter for the function instead of std::function<> seems to work wonderfully.
template<typename T, typename F>
T foldl(F f, T initial, std::vector<T> items) {
If you simply convert it to an std::function first, and then use that, it will work:
std::vector<int> arr;
arr.assign(8, 2);
std::function<int(int,int)> f = [] (int x, int y) -> int { return x * y; };
foldl(f, 1, arr);
The problem is that lambda is a different type than std::function. Each lambda is a separate type. Although a lambda (and all other function objects) is convertible to a std::function with appropriate type parameter, the compiler has no idea what template argument for T would make it convertible. (We happen to know that T = int would work, but there is no general way to figure it out.) So it cannot compile it.

Type deduction and argument passing with variadic template templates

I implemented a C++ equivalent of Python's chain function a while ago thanks to variadic templates. The function is used to iterate successively through many containers. Here is the old working version of the function using a generator named ChainedObject, whatever it is:
template<typename... Iterables>
auto chain(Iterables&&... iters)
-> ChainObject<Iterables...>
{
return /* ... */;
}
And the corresponding main:
int main()
{
std::vector<int> vec = { 1, 2, 3, 4, 5 };
std::list<int> li = { 6, 7, 8, 9, 10, 11, 12, 13 };
for (auto& i: chain(vec, li))
{
// You can edit a range of iterables
// as if there was only one of them.
i *= 5;
std::cout << i << std::endl;
}
return 0;
}
That main worked fine. We don't care what there is in ChainObject for the problem, so let's see it. I tried to use template templates to ensure that the different collections used had the same value_type and modified the function chain the following way:
template<typename T, template<typename...> class... Iterables>
auto chain(Iterables<T>&&... iters)
-> ChainObject<T, Iterables...>
{
return /* ... */;
}
I thought this would do the trick to ensure the list and vector from my previous main share a same type, but instead, I get the following error from GCC 4.7.1:
In function 'int main()':
error: no matching function for call to 'chain(std::vector&, std::list&)'
note: candidates are:
note: ChainObject<T, Iterables ...> chain(Iterables<T>&& ...) [with T = int; Iterables = {std::vector, std::list}]
note: no known conversion for argument 2 from 'std::list<int>' to 'std::list<int>&&'
note: ChainObject<T, Iterables ...> chain(Iterables<T>&& ...) [with T = int; Iterables = {std::vector, std::list}]
note: no known conversion for argument 2 from 'std::list<int>' to 'std::list<int>&&'
error: unable to deduce 'auto&' from ''
It seems that the problem comes from argument passing to the function taking rvalue references. However, I really don't understand why my first version worked fine, and note the one using template templates.
Your problem is that the T&& template magic only works for type parameters (it works by deducing T as eg. int& if needed - for lvalue arguments). It can't work for template template arguments, where the actual type is X<T>&& - X must be a class template in this case, not something like "reference-to-class-template". So in the end you have to pass a rvalue-reference, which you cannot implicitly get from a lvalue (variable).
That said, I would suggest you to revert to your earlier code and check that the value_types are the same (or compatible, etc., whatever gets you going) with SFINAE.
Rough code sketch (for strict equality):
template <class ... Ts> struct all_types_equal
{
static const bool value = false;
};
template <class T>
struct all_types_equal<T>
{
static const bool value = true;
};
template <class T, class ... Rest>
struct all_types_equal<T, T, Rest...>
{
static const bool value = all_types_equal<T, Rest...>::value;
};
template<typename... Iterables>
auto chain(Iterables&&... iters)
-> typename std::enable_if<all_types_equal<Iterable::value_type...>::value, ChainObject<Iterables...> >::type