I wonder how a compiler treats lambda functions as opposed to regular functions. Even excluding the capture-list, as I think it's called, it seems to behave slightly differently.
For example, as used in my last post, Trying to pass a constexpr lambda and use it to explicitly specify returning type, I used a constexpr lambda and passed it as a regular function parameter. I quote a part of the answer.
Parameters to constexpr functions are not themselves constexpr objects - so you cannot use them in constant expressions.
template <typename Lambda_T>
constexpr static auto foo(Lambda_T l) {
return std::array<event, (l())>{};
}
// Compiles with GCC (C++17), though ill-formed (according to the answer of my last post)
Though, what catches my eye is that this does compile passing a constexpr lambda
, but does not compile passing a constexpr regular (global) function. What causes this difference?
And are there other differences of behaviour of the compiler between regular functions and lambda-functions?
Edit: Example of Lambda-implementation
foo([](){ return 4; }); // C++17 Lambda's are implicitly constexpr
The lambda is basically a wrapper for the value in this case.
Edit: Global function
Whenever a global function is passed, the compiler will complain - as opposed to using a lambda - that this function, regardless of whether it is defined constexpr or not, cannot be used in a constant condition:
prog.cc:8:20: error: 'l' is not a constant expression
Removing some extraneous stuff from your snippet, we get
template<typename T>
constexpr void foo(T t)
{
constexpr int i = t();
}
constexpr int f() { return 42; }
auto l = []{ return 42; }
Remarks:
You are attempting to use t()
as a constexpr
within foo
. foo
could in fact be a normal function and still behave the same.
A lambda isn't a function. It is an anonymous struct with an operator()
.
struct L { constexpr int operator()() const { return 42; } };
T
is deduced as a type equivalent to L
in the call foo(l)
.
T
is deduced as int(*)()
in the call foo(f)
.
In both cases, t
isn't a constexpr within foo
.
From [expr.const]
An expression
e
is a core constant expression unless the evaluation ofe
, following the rules of the abstract machine, would evaluate one of the following expressions: [...]
an lvalue-to-rvalue conversion unless [...]
a non-volatile glvalue that refers to a non-volatile object defined with constexpr, or that refers to a non-mutable subobject of such an object, or
a non-volatile glvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of
e
;
Calling through a int(*)()
requires a lvalue-to-rvalue conversion. t
isn't an object defined with constexpr
, neither did it begin its lifetime within the evaluation of t()
. Therefore t()
isn't a constexpr
in foo(f)
.
Calling operator()
doesn't require a lvalue-to-rvalue conversion. Since there is no member access, it is simply a constexpr
function call. Therefore t()
is a constexpr
in foo(l)
.
There is one more step from core constant expressions to constant expressions, but isn't important for the discussion here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With