Some parts of your code seem pointless. I will go through them and strip them out one-by-one.
A operator+(const A& a1) {
return (+[](const A& a1, const A& a2) -> A {
return A(a1.val + a2.val);
})(*this, a1);
};
First, +[] simply decays it needlessly to a function pointer. Which can do nothing useful here, except possibly confuse the optimizer. We can also get rid of ()s:
A operator+(const A& a1) {
return [](const A& a1, const A& a2) -> A {
return A(a1.val + a2.val);
}(*this, a1);
};
now, the ->A part is noise, as it can deduce the return type:
A operator+(const A& a1) {
return [](const A& a1, const A& a2) {
return A(a1.val + a2.val);
}(*this, a1);
};
Next, why use member operator+? + is symmetric, and by using a Koenig-style operator this becomes more obvious:
friend A operator+(const A& a1, const A& a2) {
return [](const A& a1, const A& a2) {
return A(a1.val + a2.val);
}(a1, a2);
};
which gets rid of the numbering confusion you introduced (where a1 in one scope is a2 in another, and a new variable called a1 is introduced that refers to *this).
Finally, the lambda does nothing at this point:
friend A operator+(const A& a1, const A& a2) {
return A(a1.val + a2.val);
};
and deleting it results in clearer code.
Now about your question.
A compiler is more likely to optimize out the lambda as you go through these simplification steps. I suspect the worst thing you did was the unary +, which converted the lambda to a function pointer: I know gcc is good at inlining function pointers, and last I checked MSVC was bad at it. Every compiler is good at inlining a call to a stateless lambda, however.
At the point of call, the method being invoked is well known to the compiler by the type system, so no analysis of the providence of a function pointer has to be done. Data is copied to arguments then used, often in a small function. This is easy to inline.
Even if you have modest capture requirements, so long as you don't type erase or copy the lambda around, you have a bunch of local references or copies of local variables that are used in the body of the lambda. Easy to inline.
Now, each simplifying step in the first part of my answer makes the code simpler, until the end, when the lambda is gone and the only thing to "optimize" is a simple RVO (which you have to suppress with compiler flags to prevent it from happening), aka eliding the temporary A in the return statement.
If your A has an implicit constructor from the result of .val+.val, you don't even need that A in the return statement.
There isn't a simple way to write operators in terms of lambdas. You'll need to store said lambda somewhere, then glue the operator+ (or whatever) to it, those two steps are going to require more code than just injecting the body directly into the operator+.
I could do some fancy decltype and macro nonsense to let you bind a global lambda into being the body of a given operator+ for some class (using CRTP and a traits class), but calling that "simpler" is more than a bit of a stretch.
It would let you do something like:
const auto print_via_to_string = [](std::ostream& os, auto&& val){
using std::to_string;
os << to_string(decltype(val)(val));
};
struct Foo { /* ... */ };
BIND_OPERATOR( <<, std::ostream&, Foo const&, print_via_to_string );
or somesuch, and even expect the << to be inlined.
Again, considering this "simpler" is a stretch.
return A(val + a1.val);