Compilation error on passing lambda expression to template function - c++

Why can I not get the template function to accept the lambda expression?
After searching high and low -- I seriously though that this would work, but this C++ code;
template <typename F> int proc(const F& lam)
{
return lam();
}
void caller()
{
int i = 42;
int j = proc( [&i]()->int{ return i/7; } );
}
An I get the following errors;
$ g++ x.cc
x.cc: In function ‘void caller()’:
x.cc:11:44: warning: lambda expressions only available with -std=c++0x or -std=gnu++0x [enabled by default]
x.cc:11:46: error: no matching function for call to ‘proc(caller()::<lambda()>)’
x.cc:11:46: note: candidate is:
x.cc:3:27: note: template<class F> int proc(const F&)
I'm on linux using g++ 4.6.3 and 4.7.2
Does anybody know what I have to do to pass on a lambda expression as a parameter to a receiving template function? -- I don't want to use the std::function -- so my only alternative is to create an ugly functor pattern.
Update: Tried to declare the argument const F& lam, but without success.
Update2: added call to compiler...

Since the lambda isn't an lvalue, you need to pass it by const reference:
template <typename F> int proc(const F& lam)
Make sure to use -std=c++11 with g++ 4.7.2 or -std=c++0x with g++ 4.6.3.

Related

class template fails to compile when named lambda is used as template class argument or constructor argument

I'm currently experimenting with class template programming and I came across this weird behavior that I cant understand when passing a named lambda as its argument. Could somebody explain why (1) & (2) below does not work?
template<typename Predicate>
class Test{
public:
Test(Predicate p) : _pred(p) {}
private:
Predicate _pred;
};
int main(){
auto isEven = [](const auto& x){ return x%2 == 0; };
// Working cases
Test([](const auto& x){ return x%2 == 0; });
Test{isEven};
auto testObject = Test(isEven);
// Compilation Error cases
Test(isEven); // (1) Why??? Most vexing parse? not assigned to a variable? I cant understand why this fails to compile.
Test<decltype(isEven)>(isEven); // (2) Basically same as (1) but with a workaround. I'm using c++17 features, so I expect automatic class parameter type deduction via its arguments
return 0;
};
Compiler Error message: Same for (1) & (2)
cpp/test_zone/main.cpp: In function ‘int main()’:
cpp/test_zone/main.cpp:672:16: error: class template argument deduction failed:
Test(isEven);
^
cpp/test_zone/main.cpp:672:16: error: no matching function for call to ‘Test()’
cpp/test_zone/main.cpp:623:5: note: candidate: template<class Predicate> Test(Predicate)-> Test<Predicate>
Test(Predicate p): _p(p){
^~~~
cpp/test_zone/main.cpp:623:5: note: template argument deduction/substitution failed:
cpp/test_zone/main.cpp:672:16: note: candidate expects 1 argument, 0 provided
Test(isEven);
^
Please forgive my formatting, and compile error message snippet as it does not match exact lines. I'm using g++ 7.4.0, and compiling with c++17 features.
In C++, you can declare a variable as
int(i);
which is the same as
int i;
In your case, the lines
Test(isEven);
Test<decltype(isEven)>(isEven);
are compiled as though you are declaring the variable isEven. I am surprised that the error message from your compiler is so different than what I hoped to see.
You can reproduce the problem with a simple class too.
class Test{
public:
Test(int i) : _i(i) {}
private:
int _i;
};
int main(){
int i = 10;
Test(i);
return 0;
};
Error from my compiler, g++ 7.4.0:
$ g++ -std=c++17 -Wall socc.cc -o socc
socc.cc: In function ‘int main()’:
socc.cc:15:11: error: conflicting declaration ‘Test i’
Test(i);
^
socc.cc:10:9: note: previous declaration as ‘int i’
int i = 10;
As you said, this is a most vexing parse issue; Test(isEven); is trying to redefine a variable with name isEven, and same for Test<decltype(isEven)>(isEven);.
As you showed, you can use {} instead of (), this is the best solution since C++11; or you can add additional parentheses (to make it a function-style cast).
(Test(isEven));
(Test<decltype(isEven)>(isEven));
LIVE

Detecting parameter types from generic lambda - compile error with GCC

I wrote some code that retrieves the types of the non-auto parameters when given a generic lambda function. As you can see in the code below, the idea is to call the connect function with a generic lambda and provide arguments for the auto parameters (which will always be at the front in my use case). So in the code below my goal was to detect that the second parameter is of type float.
The code works fine with clang 3.8 but it doesn't compile with gcc 6.1.1, so I was wondering whether this was a bug in gcc or if this is just not valid c++ code? Can I assume that a generic lambda is implemented with a templated operator() function or is this compiler-specific?
template <typename Functor, typename... AllArgs, typename... ProvidedArgs>
void findArgTypes(void(Functor::*)(AllArgs...) const, Functor, ProvidedArgs...)
{
// AllArgs == int, float
// ProvidedArgs == int
}
template <typename Func, typename... ProvidedArgs>
void connect(Func func, ProvidedArgs... providedArgs)
{
findArgTypes(&Func::template operator()<ProvidedArgs...>, func, providedArgs...);
}
int main()
{
int tmp = 0;
connect([&](auto, float){ ++tmp; }, 0);
}
The error that gcc gives is this:
main.cpp: In instantiation of ‘void connect(Func, ProvidedArgs ...) [with Func = main()::<lambda(auto:1, float)>; ProvidedArgs = {int}]’:
main.cpp:16:33: required from here
main.cpp:11:17: error: no matches converting function ‘operator()’ to type ‘void (struct main()::<lambda(auto:1, float)>::*)() const’
findArgTypes(&Func::template operator()<ProvidedArgs...>, func, providedArgs...);
~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:16:27: note: candidate is: template<class auto:1> main()::<lambda(auto:1, float)>
connect([](auto, float){}, 0);
^
Removing the const in findArgTypes gives the same result.
Using the following code works with both compilers:
struct Foo
{
template <typename T>
void operator()(T, float) const {}
};
int main()
{
Foo f;
connect(f, 0);
}
You have error because you are expecting functor (object) but lambda with empty capture is convertible to free function:
int main() {
using function = void (*)(int, float);
function a = [](auto, float){};
}
See lambda from cppreference:
For the newest version of your question that implementation satisfies both compilers:
template <typename Func, typename... ProvidedArgs>
void connect(Func func, ProvidedArgs... providedArgs)
{
auto mf = &Func::template operator()<ProvidedArgs...>;
findArgTypes(mf, func, providedArgs...);
}
I think this is gcc compiler bug that gcc needs this auto local variable to work correctly...
BTW, one question - one bug in clang, one in gcc - I really advice you to find simpler way to achieve your goals - maybe consider to just use std::function instead of quite fresh generic-lambda?

Removing unnecessary template argument, but doesn't compile anymore

I have two different approaches to a templated function using auto and decltype but one of them does not compile, yielding two error:
Where is the main difference between the following versions of the code:
This compiles
template<typename MemoryMapper, typename EngineParts>
class EngineBase {
const MemoryMapper memoryMapper;
public:
const EngineParts engineParts;
EngineBase();
virtual ~EngineBase();
template<typename Mapper, typename MemoryAccessor>
void getMemoryManager(void);
template<MemoryMapper,typename MemoryAccessor>
auto getMemoryManager(void)->decltype(memoryMapper.getMemoryManager<MemoryAccessor>())
{
return memoryMapper.getMemoryManager<MemoryAccessor>();
}
};
While this does not
template<typename MemoryMapper, typename EngineParts>
class EngineBase {
const MemoryMapper memoryMapper;
public:
const EngineParts engineParts;
EngineBase();
virtual ~EngineBase();
template<typename MemoryAccessor>
auto getMemoryManager(void)->decltype(memoryMapper.getMemoryManager<MemoryAccessor>())
{
return memoryMapper.getMemoryManager<MemoryAccessor>();
}
};
The error output is as follows:
g++ -O0 -g3 -Wall -c -fmessage-length=0 -std=c++0x -o EngineBase.o "..\\EngineBase.cpp"
In file included from ..\EngineBase.cpp:7:0:
..\EngineBase.h:22:84: error: expected primary-expression before '>' token
auto getMemoryManager(void)->decltype(memoryMapper.getMemoryManager<MemoryAccessor>())
^
..\EngineBase.h:22:86: error: expected primary-expression before ')' token
auto getMemoryManager(void)->decltype(memoryMapper.getMemoryManager<MemoryAccessor>())
^
14:50:52 Build Finished (took 2s.587ms)
Since I don't want to specifically write the first template argument on every use and it would create the impression that there are more functions defined than realistically are, how can I get the second version to work without introducing another template argument? Function parameters would be ok, though.
template<typename MemoryAccessor>
auto getMemoryManager(void)->decltype(memoryMapper.template getMemoryManager<MemoryAccessor>())
// ~~~~~~~^
{
return memoryMapper.template getMemoryManager<MemoryAccessor>();
// ~~~~~~~^
}

c++11 lambdas doesn't work with boost::concepts

The following code doesn't work with clang 3.3. but it does word with g++ 4.8.1. Boost version is 1.55.
#include <boost/concept_check.hpp>
template <typename X>
class ConceptsBase {};
int main() {
auto l = [](){};
BOOST_CONCEPT_ASSERT((ConceptsBase<decltype(l)>));
return 0;
}
g++ -std=c++0x test.cpp -I /home/wygos/libs/boost_1_55_0/include/ # works fine!
clang++ -std=c++0x test.cpp -I /home/wygos/libs/boost_1_55_0/include/
gives:
test.cpp:9:5: error: non-type template argument refers to function 'failed' that does not have linkage
BOOST_CONCEPT_ASSERT((ConceptsBase<decltype(l)>));
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/wygos/libs/boost_1_55_0/include/boost/concept/assert.hpp:44:5: note: expanded from macro 'BOOST_CONCEPT_ASSERT'
BOOST_CONCEPT_ASSERT_FN(void(*)ModelInParens)
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/wygos/libs/boost_1_55_0/include/boost/concept/detail/general.hpp:70:6: note: expanded from macro 'BOOST_CONCEPT_ASSERT_FN'
&::boost::concepts::requirement_<ModelFnPtr>::failed> \
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/wygos/libs/boost_1_55_0/include/boost/concept/detail/general.hpp:38:17: note: non-type template argument refers to function here
static void failed() { ((Model*)0)->~Model(); }
My lucky guess is that it might be connected to:
http://llvm.org/bugs/show_bug.cgi?id=17030

std::function in template function

I was trying to write a template function which can accept functor as parameter and call it afterwards. The program is as follows:
#include <iostream>
#include <functional>
using namespace std;
template<typename R, typename... Args>
R call(function<R(Args...)> fun, Args... args)
{
cout << "call# " << __LINE__ <<endl;
return fun(args...);
}
int main()
{
cout << call(std::plus<int>(),1,2) <<endl;
return 0;
}
The G++ compplains:
g++ -c -Wall -std=c++0x -I../include a.cpp -o a.o
a.cpp: In function ‘int main()’:
a.cpp:16:38: error: no matching function for call to ‘call(std::plus<int>, int, int)’
a.cpp:16:38: note: candidate is:
a.cpp:7:3: note: template<class R, class ... Args> R call(std::function<_Res(_ArgTypes ...)>, Args ...)
a.cpp:7:3: note: template argument deduction/substitution failed:
a.cpp:16:38: note: ‘std::plus<int>’ is not derived from ‘std::function<_Res(_ArgTypes ...)>’
make: *** [a.o] Error 1
I suppose std::plus<int>() could be deduced to std::function<int(int,int)>, but it didn't. Why was that? GCC is gcc version 4.7.2 20120921 (Red Hat 4.7.2-2) (GCC)
I suppose std::plus() could be deduced to std::function
No. It could not be deduced given that you have passed an object of type std::plus<int>.
In your case, you do not need to use std::function, as generally you would mostly use it when storing different functions/function objects that can be called with a specific signature.
With that, you can just have your call function accept the function/function object directly, with its original type deduced, without using std::function. Also, you might also want to use perfect forwarding when accepting the parameters and use std::forward when passing them as arguments to the function/function object. You should also use the return type of your function as the return type of call. Use C++11's trailing return type with decltype for that.
#include <iostream>
#include <functional>
using namespace std;
template<typename R, typename... Args>
auto call(R fun, Args&&... args) -> decltype(fun(std::forward<Args>(args)...))
{
cout << "call# " << __LINE__ <<endl;
return fun(std::forward<Args>(args)...);
}
int main()
{
cout << call(std::plus<int>(),1,2) <<endl;
return 0;
}
LIVE CODE
As what #Jan Hudec has commented, __LINE__ in there will always result the same in all calls to call, whatever function is passed.
It can't deduce the template arguments.
I would recommend changing the function signature like so:
template<typename F, typename... Args>
auto call(F fun, Args... args )
-> decltype( fun(args...) )
Most implicit conversions are not considered when deducing template arguments. Certainly not user-defined ones. So even if plus is convertible to function, it doesn't make a difference.