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 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.
Lambda expressions as class template parameters in C++14
No the situation in C++14 has not changed at all and in fact the language in section 5.1.2
Lambda expressions paragraph 2 has been tightened from:
A lambda-expression shall not appear in an unevaluated operand (Clause
5).
to:
[...]A lambda-expression shall not appear in an unevaluated operand
(Clause 5), in a templateargument, in an alias-declaration, in a
typedef declaration, or in the declaration of a function or function
template outside its function body and default arguments. [ Note: The
intention is to prevent lambdas from appearing in a signature. —end
note ][...]
Defect report 1607. Lambdas in template parameters lead to this change.
The defect report only obliquely deals with the rationale for disallowing this but we can find a very detailed explanation for why this is disallowed in Rationale for lambda-expressions not being allowed in unevaluated contexts. The reasons boil down to:
- Lambda expressions not having a unique type
- Compiler implementation issues:
- Such as an extraordinary expansion of
SFINAE
- The possible requirement to name mangle the whole body of a lambda.
- Such as an extraordinary expansion of
Given the rationale for this restriction it seems unlikely to change.
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;
}
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.
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 two lambda functions using a single template parameter in C++
What's the problem ?
If I understand well your question, when you define splot()
, using a different template parameter for each lambda passed, it compiles perfectly:
template <class F1, class F2>
double splot(F1 g, F2 l, double x, double sigma, double gamma) {
...
}
But both lambdas that you use have the same signature (same argument types and same return type) so you expect them to be of the same type, and the following definition of splot()
to compile as well:
template <class F>
double splot(F g, F l, double x, double sigma, double gamma) {
...
}
But it doesn't compile, and the compiler makes it even confusing with an error message that suggests that both lambdas have a different type, whereas the displayed type name indicates the same type:
note: template argument deduction/substitution failed:
note: deduced conflicting types for parameter ‘F’ (‘main()::<lambda(double, double)>’ and ‘main()::<lambda(double, double)>’)
Why is there a problem ?
The compiler is right despite the misleading error message. There is an error in the type deduction with F
: the C++ standard states in [expr.prim.lambda.closure]/1
that:
The type of a lambda-expression (which is also the type of the closure
object) is a unique, unnamed non-union class type, called the closure
type, whose properties are described below.
So every lambda has a different type, even if they share the same signature.
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);
Related Topics
Constant Expression Initializer for Static Class Member of Type Double
Rvalue to Lvalue Conversion Visual Studio
How to Programmatically Cause a Core Dump in C/C++
When Do I Really Need to Use Atomic<Bool> Instead of Bool
Inheritance or Composition: Rely on "Is-A" and "Has-A"
Gcc Equivalent of Ms's /Bigobj
The Behaviour of Floating Point Division by Zero
Writing Directly to Std::String Internal Buffers
How to Signify No More Input for String Ss in the Loop While (Cin >> Ss)
How to Set Timeout for Std::Cin
Inlining Failed in Call to Always_Inline '_M256D _Mm256_Broadcast_Sd(Const Double*)'
How to Find Where an Exception Was Thrown in C++
How Can Currying Be Done in C++
(Partially) Specializing a Non-Type Template Parameter of Dependent Type
Why Doesn't Reference-To-Member Exist in C++