Lambda expressions as class template parameters
As of C++20, this answer is now outdated. C++20 introduces stateless lambdas in unevaluated contexts1:
This restriction was originally designed to prevent lambdas from appearing in signatures, which would have opened a can of worm for mangling because lambdas are required to have unique types. However, the restriction is much stronger than it needs to be, and it is indeed possible to achieve the same effect without it
Some restrictions are still in place (e.g. lambdas still can't appear on function signatures), but the described usecase is now completely valid and the declaration of a variable is no longer necessary.
I'm asking if you can do something like:
Foo<decltype([]()->void { })> foo;
No you can't, because lambda expressions shall not appear in an unevaluated context (such as decltype
and sizeof
, amongst others).
C++0x FDIS, 5.1.2 [expr.prim.lambda] p2
The evaluation of a lambda-expression results in a prvalue temporary (12.2). This temporary is called the
closure object. A lambda-expression shall not appear in an unevaluated operand (Clause 5). [ Note: A
closure object behaves like a function object (20.8).—end note ]
(emphasis mine)
You would need to first create a specific lambda and then use decltype on that:
auto my_comp = [](const std::string& left, const std::string& right) -> bool {
// whatever
}
typedef std::unordered_map<
std::string,
std::string,
std::hash<std::string>,
decltype(my_comp)
> map_type;
That is because each lambda-derived closure object could have a completely different type, they're like anonymous functions after all.
How to use a lambda expression as a template parameter?
The 2nd template parameter of std::set
expects a type, not an expression, so it is just you are using it wrongly.
You could create the set like this:
auto comp = [](const A& lhs, const A& rhs) -> bool { return lhs.x < rhs.x; };
auto SetOfA = std::set <A, decltype(comp)> (comp);
How I can pass lambda expression to c++ template as parameter
A lambda is not a function pointer! A lambda is an instance of compiler generated class!
However, a non capturing lambda may be converted to a function pointer using it's operator+
Here's an example:
int main() {
auto lambda = [](int a) { return a; };
func ptr = +lambda; // this would work
return 0;
}
Sadly, the operator+
won't even work in your case because it has not been declared as constexpr, so you can't use it in a template parameter.
A fix to your case would be to use a free function... until N4487 is not accepted, you can't expect to pass lambda as template parameter.
Another fix would be to create your own functor instead of a lambda:
struct LambdaType {
constexpr LambdaType() = default;
int operator()(int a) {
return run(a);
}
// this is a non-capturing lambda, the operator can be
// in a static function
static int run(int a) {
return a;
}
};
int main() {
LambdaType lambda;
function<&LambdaType::run>(1); // ---> this is working
return 0;
}
This solution is not quite appealing, but it might be useful if LambdaType
is hidden in a cpp file.
If your goal is only the compiler to be able to inline your code, you can use templates to pass the lambda around:
#include <iostream>
template <typename T>
int function(T foo, int a) {
return foo(a);
}
int main() {
int a;
std::cin >> a;
int b = function([](int a) { return a; }, a);
return b;
}
Since the compiler knows the type of T
for each instanciation, a good compiler should be able to optimize out the lambda.
With clang, the third option gives the following assembly:
main: # @main
pushq %rax
leaq 4(%rsp), %rsi
movl std::cin, %edi
callq std::basic_istream<char, std::char_traits<char> >::operator>>(int&)
movl 4(%rsp), %eax # this is the call to the function
addq $8, %rsp
retq
pushq %rax
movl std::__ioinit, %edi
callq std::ios_base::Init::Init()
movl std::ios_base::Init::~Init(), %edi
movl std::__ioinit, %esi
movl $__dso_handle, %edx
popq %rax
jmp __cxa_atexit # TAILCALL
I used -std=c++14 -Ofast -march=native
as flags.
C++ Pass lambda to template parameter
Lambdas in C++14, including their conversion to function pointers, are not constexpr.
In C++17, this is going to change. There are no stable compilers with that feature implemented that I'm aware of (if you find one, can you mention it in the comments below?).
At that point
constexpr auto tmp = []() -> void { std::cout << "Hello world\n"; };
function<+tmp>();
will definitely work. I am uncertain if
function<+[]() -> void { std::cout << "Hello world\n"; }>()
would work; there are some rules about lambdas in unevaluated contexts and inside template argument lists that may be separate from the constexpr
lambda problem and may apply here.
We can hack it in C++14.
Create a template class that stores a static copy of a lambda and exposes a static function with the same signature (f_ptr
) that calls that static copy of a lambda.
Instantiate it once globally with your lambda.
Pass a pointer to the f_ptr
to your template.
So:
template<class L> struct stateless; // todo
template<class L> stateless<L> make_stateless(L l){return std::move(l);}
auto foo = make_stateless( []() -> void { std::cout << "Hello world\n"; } );
function< &foo::f_ptr >();
this is almost certainly not what you want.
How to pass a lambda to a template paramater
In C++11? With a lambda as template parameter?
Given that a lambda (before C++20) can't be used in an unevaluated context (so in a decltype()
) and can't be default constructed, I don't see a way.
The best I can imagine, in C++11, to reproduce something similar is something as follows
template <typename T, typename F = std::function<std::string(T const &)>>
std::string func (T const & t, F f = [](const T &t){return std::string{t};})
{ return f(t); }
that you can call with only the first argument
func("abc");
given that you accept the default lambda (but is saved as a std::function
).
You can also pass another lambda, if you don't like the default one
func(1, [](int const &){ return "one"; });
but as a traditional argument, not as template parameter.
Lambda as template parameter
As the compiler is helpfully telling you, the problem is with this line:
return Order(val, other.val);
Since Order
is a type (and not a function), that is calling Order's two-argument constructor. But it doesn't have one.
The correct syntax for invoking a functional class is:
return Order()(val, other.val);
However, that won't work either because the class generated for the lambda has a deleted default constructor.
In short, you need to instantiate your class with (the only) instance of the lambda.
Here's one possible way to proceed:
template<typename Order>
struct foo {
foo(Order compare) : compare_(compare) {}
bool operator<(const foo& other) {
return compare_(val, other.val);
}
int val;
Order compare_;
};
/* In practice, you'll want to template a parameter pack
* for the other arguments to the constructor, since those are likely.
* Also, you might want to use std::forward.
*/
template<typename Order>
foo<Order> foomaker(Order order) {
return foo<Order>(order);
}
int main() {
auto GoLess = [](int a,int b) -> bool
{
return a < b;
};
auto a = foomaker(GoLess);
auto b = foomaker(GoLess);
bool r = a < b;
return r;
}
Can a lambda instantiate a template function?
The answer to your question is technically yes, lambda bodies can instantiate template functions. The actual example doesn't work, because int n
as a parameter can't be used that way.
There is an easy workaround
template<auto x>
using constant_t = std::integral_constant< std::decay_t<decltype(x)>, x >;
template<auto x>
constexpr constant_t<x> constant = {};
template <int n> int fn () { int arr[n] = {0}; return sizeof(arr); }
auto fx = [] (auto n) { return fn<n>(); };
std::cout << fx( constant<3> );
Live example.
Here I made the constant<x>
variable template that creates an instance of std::integral_constant<X, x>
. This is a stateless (but not valueless!) type that has a constexpr conversion to its value.
We can pass that to a lambda, and so long as the lambda takes it by value we can then convert it to a constexpr
value within the lambda, including passing it as a template non-type parameter, instantiating a template function specialization as you are asking for.
The can be done without the constant
variable template, ie if you don't have auto
parameter support:
template<std::size_t N>
using index_t = std::integral_constant<std::size_t, N>;
template<std::size_t N>
constexpr index_t<N> index = {};
we can use a type-specific version of it, and just pass that, and it works the same way.
Aside, constant<?>
is fun. For example:
using upFILE=std::unique_ptr<
std::FILE,
constant_t<std::fclose>
>;
upFILE file( fopen("hello.txt", "r") );
does the right thingtm.
Is it possible to explicitly specify template arguments in a generic lambda passed to a function?
In a generic lambda, operator()
is a template, but the lambda type is not.
Instead of instantiating a template at an index F<I>{}()
, one needs to instantiate operator()
at an index. Since the lambda has captures, one will need to pass it instead of just the type as a template argument.
Replace:
template <template<std::size_t> typename F, std::size_t... I>
void iterator_implementation(
const std::index_sequence<I...> /*unused*/)
{
((F<I>{}()), ...);
}
with:
template <typename F, std::size_t... I>
void iterator_implementation(F f,
const std::index_sequence<I...> /*unused*/)
{
((f.template operator()<I>()), ...);
}
Full example: https://godbolt.org/z/rYTP1KTYc
How to invoke a lambda template?
The second definition is a variable template. It does not define the lambda's operator()
as a template, but rather takes the parameter pack for the argument types of operator()
. The resulting operator()
is a regular member function of the instantiated variable's closure type. There is no template argument deduction possible here.
So when you write lamd<int>
, the variable gets a closure type with a operator()(int)
, not something callable with 3 integers.
As mentioned already, you can use a generic lambda instead.
In C++20, if you'd need the lambda's argument types to be named and deduced, you can use the syntax:
auto lamd = []<typename... Pack>(Pack...) {}
This will define the operator as template, accepting a parameter pack, and leave the door open for template argument deduction.
Related Topics
How Does Q_Foreach (= Foreach) MACro Work and Why Is It That Complex
Efficiency of Postincrement V.S. Preincrement in C++
Shift Image Content with Opencv
Creating a String List and an Enum List from a C++ MACro
Clean Up Your #Include Statements
Filestorage for Opencv Python API
Is There a Reason Declval Returns Add_Rvalue_Reference Instead of Add_Lvalue_Reference
Comparing Two Integers Without Any Comparison
What's the Point of Std::Unique_Ptr::Get
How to Determine the Correct Size of a Qtablewidget
Ensuring C++ Doubles Are 64 Bits
C++ Unordered_Map Fail When Used with a Vector as Key
Getprocaddress Function in C++
How to Test If a Constexpr Function Is Evaluated at Compile Time