11

I'd like to do something like this (inside a class):

static constexpr MyStruct ops[6] = {
    {'+', [&] (double a, double b) { return a+b; } },
    {'-', [&] (double a, double b) { return a-b; } },
    ...
};

Where MyStruct is defined as:

typedef double (*binOp)(double, double);
struct MyStruct {
    char c;
    binOp fn;
};

I also tried:

std::function <double(double,double)> fn;

for the definition of fn, but no luck.

The error I get for the first case is "error: field initializer is not constant" which I don't really get. If I try with std::function it gets worse, since it says: "cannot be initialized by a non-constant expression when being declared".

Why is the lambda function non-constant? Am I missing something?

YePhIcK
  • 5,816
  • 2
  • 27
  • 52
David G.F.
  • 162
  • 1
  • 8
  • 1
    Replace `constexpr` with `const`. – Nawaz May 29 '15 at 11:07
  • 4
    lambda-expressions currently may not occur inside constant expressions, but that restriction might get removed eventually: https://isocpp.org/files/papers/N4487.pdf – dyp May 29 '15 at 11:11
  • 1
    From C++17, please note that lambdas can now be constexpr: https://stackoverflow.com/questions/6420085/is-constexpr-supported-with-lambda-functions-expressions – Guillaume D Jan 17 '19 at 15:27
  • 1
    Also note your lambda's capture everything in their scope by reference. Currently they don't use anything so it's not a problem, but capturing lambda's are not convertible to a raw function pointer. – rubenvb Mar 17 '20 at 12:56

2 Answers2

8

When you construct constexpr object, everything you pass into it needs to be a core constant expression, [decl.constexpr]/9:

A constexpr specifier used in an object declaration declares the object as const. Such an object shall have literal type and shall be initialized. If it is initialized by a constructor call, that call shall be a constant expression (5.19).

and, from [expr.const] lambdas are not constant expressions1:

A conditional-expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine (1.9), would evaluate one of the following expressions:

  • [...]
  • a lambda-expression (5.1.2);
  • [...]

However, that applies only to constexpr and not to const, so you could simply do that instead:

static const MyStruct ops[6] = {
    {'+', [] (double a, double b) { return a+b; } },
    {'-', [] (double a, double b) { return a-b; } },
};

Note: your lambdas don't need to capture anything, so you should just empty the capture list [].


1As dyp points out, there is a proposal to change this: N4487
Community
  • 1
  • 1
Barry
  • 286,269
  • 29
  • 621
  • 977
1

capturing lambda cannot decay to function pointer.

and operator to return the function pointer from a (non-capturing) lambda is not constexpr.

Jarod42
  • 203,559
  • 14
  • 181
  • 302