Using Lambdas in Maps - c++

I'm trying to implement a map with a lambda function in C++11 as such
std::map<int, int, [](const int&a, const int& b) { return a < b; }> test;
but that fails with
error: type/value mismatch at argument 3 in template parameter list for ‘template<class _Key, class _Tp, class _Compare, class _Alloc> class std::map’
error: expected a type, got ‘{}’
error: invalid type in declaration before ‘;’ token
Any advice?

You need to pass the type of the lambda as a template argument, not the lambda itself. What you want is this:
auto mycomp = [](const int&a, const int& b) { return a < b; };
std::map<int, int, decltype(mycomp)> test(mycomp);
Although in fact, since your lambda has no captures, it can actually be stored in a function pointer, so alternatively, you could do this:
std::map<int, int, bool(*)(const int&,const int&)>
test([](const int&a, const int& b) { return a < b; });
Though I find the first much more readable. Although using the function pointer type is more versatile. i.e. It can accept any function pointer or non-capturing lambda that matches that signature. But if you change your lambda to be capturing, it will not work. For a more versatile version, you could use std::function, i.e:
std::map<int, int, std::function<bool(const int&, const int&)>>
That will work with any function, lambda(capturing or not) or function object, as long as the signature matches.

Related

Passing a lambda function to a template method

I have the following templated method:
auto clusters = std::vector<std::pair<std::vector<long>, math::Vector3f>>
template<class T>
void eraserFunction(std::vector<T>& array, std::function<int(const T&, const T&)> func)
{
}
And I have a function that looks like
auto comp1 = [&](
const std::pair<std::vector<long>, math::Vector3f>& n1,
const std::pair<std::vector<long>, math::Vector3f>& n2
) -> int {
return 0;
};
math::eraserFunction(clusters, comp1);
However, I get a syntax error saying:
116 | void eraserFunction(std::vector<T>& array, std::function<int(const T&, const T&)> func)
| ^~~~~~~~~~~~~~
core.hpp:116:6: note: template argument deduction/substitution failed:
geom.cpp:593:23: note: 'math::method(const at::Tensor&, const at::Tensor&, int, float, int, int, float)::<lambda(const std::pair<std::vector<long int>, Eigen::Matrix<float, 3, 1> >&, const std::pair<std::vector<long int>, Eigen::Matrix<float, 3, 1> >&)>' is not derived from 'std::function<int(const T&, const T&)>'
593 | math::eraserFunction(clusters, comp1);
The function call tries to deduce T from both the first and second function parameter.
It will correctly deduce T from the first parameter, but fail to deduce it from the second parameter, because the second function argument is a lambda type, not a std::function type.
If deduction isn't possible from all parameters that are deduced context, deduction fails.
You don't really need deduction from the second parameter/argument here, since T should be fully determined by the first argument. So you can make the second parameter a non-deduced context, for example by using std::type_identity:
void eraserFunction(std::vector<T>& array, std::type_identity_t<std::function<int(const T&, const T&)>> func)
This requires C++20, but can be implemented easily in user code as well if you are limited to C++11:
template<typename T>
struct type_identity { using type = T; };
and then
void eraserFunction(std::vector<T>& array, typename type_identity<std::function<int(const T&, const T&)>>::type func)
std::identity_type_t<T> is a type alias for std::identity_type<T>::type. Everything left to the scope resolution operator :: is a non-deduced context, which is why that works.
If you don't have any particular reason to use std::function here, you can also just take any callable type as second template argument:
template<class T, class F>
void eraserFunction(std::vector<T>& array, F func)
This can be called with a lambda, function pointer, std::function, etc. as argument. If the argument is not callable with the expected types, it will cause an error on instantiation of the function body containing the call. You can use SFINAE or since C++20 a type constraint to enforce this already at overload resolution time.

Why am I getting "error: no match for ‘operator->*’" when the parameters on both sides look correct?

I'm trying to call a function from within a template function inside a template.
The call itself, however, doesn't compile, instead I get the following error:
/home/alexis/tmp/b.cpp: In instantiation of ‘bool callback_manager<C>::call_member(F, ARGS ...) [with F = bool (main()::foo::*)(int, int, int); ARGS = {int, int, int}; C = std::vector<std::shared_ptr<main()::foo> >]’:
/home/alexis/tmp/b.cpp:43:47: required from here
/home/alexis/tmp/b.cpp:15:19: error: no match for ‘operator->*’ (operand types are ‘std::shared_ptr<main()::foo>’ and ‘bool (main()::foo::*)(int, int, int)’)
if(!(c->*func)(&args...))
~~^~~~~~~~
Here is a simplified version of the code I'm trying to compile:
#include <memory>
#include <vector>
template<typename C>
class callback_manager
{
public:
template<typename F, typename ... ARGS>
bool call_member(F func, ARGS ... args)
{
C callbacks(f_callbacks);
for(auto c : callbacks)
{
if(!(c->*func)(args...))
{
return false;
}
}
return true;
}
private:
C f_callbacks;
};
int main()
{
class foo
{
public:
typedef std::shared_ptr<foo> pointer_t;
typedef std::vector<pointer_t> vector_t;
bool the_callback(int, int, int)
{
return true;
}
};
callback_manager<foo::vector_t> m;
m.call_member(&foo::the_callback, 5, 13, 7);
return 1;
}
Looking at the parameters, it seems to be that both are correct:
std::shared_ptr<main()::foo>
and
bool (main()::foo::*)(int, int, int)
The fact is that the ->* operator doesn't work with the std::shared_ptr<>.
The solution is to retrieve the bare pointer like so:
if(!(c.get()->*func)(args...)) ...
It then compiles as expected.
You can also rewrite it as follow, which I think is more cryptic:
if(!(*c).*func)(args...)) ...
(i.e. the shared_ptr::operator * () function returns the pointed to object held by the shared pointer, hence the .* operator is used in this case.)
Replace
if(!(c->*func)(args...))
with
if(!(std::cref(func)(c, args...)))
to use the INVOKE machinery of C++. Or use std::invoke directly.
INVOKE concept in the standard, and std::invoke, where designed to work with pmfs and smart pointers.
Meanwhile, ->* isn't overloaded by smart pointers. So direct use like that won't work.
As a side benefit, now a non member function can be passed in as the func.

Lambda for compare function in c++ not working

This code produces an error:
priority_queue<int, vector<int>, [](int a, int b)->bool{return a>b;}> q;
Why?
(I know that for things like this I can just use std::greater or the default sorting, but I'm trying to learn how to create a custom comparator)
2 errors generated:
error: no matching function for call to object of type lambda
error: template argument for template type parameter must be a type
You need to specify the type, but not the lambda expression itself as the template argument. And lambda should be specified as constructor argument.
E.g.
auto c = [](int a, int b)->bool{return a>b;}; // declare lambda in advance
priority_queue<int, vector<int>, decltype(c)> q(c);
// ^^^^^^^^^^^ <- specify the type of lambda
// ^ <- specify the lambda as constructor argument
You can use std::function instead of lambda
auto q = std::priority_queue<int, std::vector<int>, std::function<bool(const int&, const int&)>>{
[](const int& a, const int& b)
{
return a < b;
}
};

C++ Templates: correct way to return a new type

Sorry for the generic title, but I'm unable to focus the problem.
I have a templatized class method that accept an argument pack and provides a new type in return, to hide the details of the implementation. More specifically, the class handles SQLite queries, and the method calls sqlite3_prepare() to prepare the statement before executing the query.
class Table {
...
template <typename ...Ts>
class PreparedStatement { ... };
template <typename ...Ts>
PreparedStatement<Ts...> prepare(std::tuple<Ts...> tuple) {
// do something
return PreparedStatement<Ts...> ( ... );
}
That works well with "normal" types, but the problem occurs when the arguments are declared const:
const Field<int> fld = createField<int>("name");
...
PreparedStatement<decltype(fld)> s = prepare(make_tuple(fld));
The error is the following:
no match for 'operator =' (operand types are PreparedStatenent<const Field<int>> and PreparedStatement<Field<int>>
I suspect the issue is in my declaration of the function, is there a way to fix this issue and make the function more "elegant" ?
NOTE: I know I can fix the issue by manually declare the s variable, but my doubts are on how the method was implemented.
As Many Asked, here's an example:
#include <tuple>
template <typename T>
struct Field {
};
class Table {
public:
template <typename ...Ts>
class PreparedStatement {
public:
PreparedStatement() {};
};
template <typename ...Ts>
PreparedStatement<Ts...> prepare(std::tuple<Ts...> tuple) {
// do something
return PreparedStatement<Ts...> ( );
}
};
int main()
{
Field<int> fld;
Table t;
Table::PreparedStatement<decltype(fld)> p;
p = t.prepare(std::make_tuple(fld));
// here comes the problem
const Field<int> f2;
Table::PreparedStatement<decltype(f2)> p2;
p2 = t.prepare(std::make_tuple(f2));
return 0;
}
and here's the compiler output
main.cpp: In function 'int main()': main.cpp:35:39: error: no match
for 'operator=' (operand types are 'Table::PreparedStatement >' and 'Table::PreparedStatement >')
p2 = t.prepare(std::make_tuple(f2));
^ main.cpp:10:10: note: candidate: constexpr Table::PreparedStatement >&
Table::PreparedStatement >::operator=(const
Table::PreparedStatement >&)
class PreparedStatement {
^~~~~~~~~~~~~~~~~ main.cpp:10:10: note: no known conversion for argument 1 from 'Table::PreparedStatement >'
to 'const Table::PreparedStatement >&'
main.cpp:10:10: note: candidate: constexpr
Table::PreparedStatement >&
Table::PreparedStatement
::operator=(Table::PreparedStatement >&&) main.cpp:10:10: note: no known conversion for argument 1 from
'Table::PreparedStatement >' to
'Table::PreparedStatement >&&'
UPDATE
As many noted, I could use auto to deduce the type, but in some condition auto cannot practically be used. One is, for example, if I need to declare the statement in the Class Context.
So suppose auto is forbidden for some reason. Isn't any other solution available? See the updated code above.
cppreference.com for make_tuple tells us:
template< class... Types >
tuple<VTypes...> make_tuple( Types&&... args );
For each Ti in Types..., the corresponding type Vi in Vtypes... is
std::decay<Ti>::type unless application of std::decay results in
std::reference_wrapper<X> for some type X, in which case the deduced
type is X&.
While std::decay, among other things, removes cv-qualifiers. So your type will be no PreparedStatement<const Field<int>>, but PreparedStatement<Field<int>>.
You can use auto, as manni66 proposed, to avoid such problems.
auto s = prepare(make_tuple(fld));
I could use auto to deduce the type, but in some condition auto cannot practically be used. One is, for example, if I need to declare the statement in the Class Context. So suppose auto is forbidden for some reason. Isn't any other solution available? See the updated code above.
Instead of auto, you can use a decltype expression that take in count the value returned by prepare.
I mean... instead of
Table::PreparedStatement<decltype(f2)> p2;
you can try with
decltype(t.prepare(std::make_tuple(f2))) p2;
or
decltype(std::declval<Table>().prepare(
std::make_tuple(std::declval<Field<int>>()))) p2;
I suppose you can use a similar decltype() also to declare members of your classes.

Boost sub_range with for_each; why am I getting a const-reference?

On OS X Mavericks, using boost 1.55.0 and clang-500.2.79 (based on LLVM 3.3svn), I'm trying to iterate over a sub-range in a std::map using boost::for_each and boost:sub_range. In my function-object, I expect to receive a std::pair &. Instead, I seem to receive a const std::pair &. Why?
#include <map>
#include <boost/range/algorithm.hpp>
#include <boost/range/sub_range.hpp>
using std::map;
using std::begin;
using std::end;
using std::pair;
using boost::for_each;
using boost::sub_range;
int main()
{
map<int, int> myMap;
sub_range<decltype(myMap)> s{
begin(myMap),
end(myMap)
};
auto f1 = [&](const pair<int, int> &) {
};
for_each(s, f1); // Compiles fine
auto f2 = [&](pair<int, int> &) {
};
for_each(s, f2); // Fails to compile
}
/Users/ambarish> clang++ main.cxx
In file included from main.cxx:1:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/map:371:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/__tree:18:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/algorithm:793:9: error: no matching function for call to object of type '<lambda at main.cxx:24:15>'
__f(*__first);
^~~
/usr/local/include/boost/range/algorithm/for_each.hpp:80:12: note: in instantiation of function template specialization
'std::__1::for_each<std::__1::__map_iterator<std::__1::__tree_iterator<std::__1::pair<int, int>, std::__1::__tree_node<std::__1::pair<int, int>, void *> *, long> >, <lambda at main.cxx:24:15> >'
requested here
return std::for_each<
^
main.cxx:26:5: note: (skipping 1 context in backtrace; use -ftemplate-backtrace-limit=0 to see all)
for_each(s, f2);
^
main.cxx:24:15: note: candidate function not viable: no known conversion from 'value_type' (aka 'pair<__key_type, __mapped_type>') to 'pair<int, int> &' for 1st argument
auto f2 = [&](pair<int, int> &) {
^
1 error generated.
It doesn't compile because the value type of std::map<int, int> is not std::pair<int,int> but std::pair<const int, int>.
The reason first one (f1) compiles is because std::pair has got this constructor:
template< class U1, class U2 >
pair( pair<U1, U2>&& p );
and because f1 takes the argument by const reference. Now there's a suitable conversion which produces a temporary that can bind to a const reference easily.
Fix:
auto f2 = [&](pair<const int, int> &) { };
// or
auto f2 = [&](pair<int, int>) { };
The code appears to be using std::for_each in the boost library implementation, and C++11 style standard lambdas are being passed to the for_each function at any rate. It really depends on how the library passes the container's iterators to the std::for_each