21

I found this piece of code doesn't work:

typedef int (*fp)(int a, int b);

constexpr fp addition()
{
    return [](int a, int b){ return a+b; };
}

#include <iostream>

int main()
{
    fp fun = addition();
    std::cout << fun(2,2);
}

It gives me error

cexpr.cpp: In function 'constexpr int (* addition())(int, int)':
cexpr.cpp:5:43: error: call to non-constexpr function 'addition()::<lambda(int,
int)>::operator int (*)(int, int)() const'

Why is that? I'm not calling it here.

Direct approach works:

typedef int (*fp)(int a, int b);

#include <iostream>

int main()
{
    fp fun = [](int a, int b){ return a+b; };
    std::cout << fun(2,2);
}

I'm using MinGW with g++ version 4.7.2.

4
  • 3
    The implicit conversion to function pointers isn't constexpr apparently. Commented Dec 29, 2012 at 21:12
  • @Pubby changing it to return static_cast<fp>([](int a, int b){ return a+b; });, and it still doesn't work. Commented Dec 29, 2012 at 21:14
  • @Pubby: That is not a problem. constexpr int f(int a) { return a; } is absolutely fine! Commented Dec 29, 2012 at 21:19
  • The conversion function? Yes, you are calling it. Commented Dec 29, 2012 at 21:31

3 Answers 3

9

Your function fp() does not return a literal type, therefore it cannot be a constexpr function:

From 7.1.5: "The definition of a constexpr function shall satisfy the following constraints:

  • it shall not be virtual (10.3);
  • its return type shall be a literal type;
  • each of its parameter types shall be a literal type;
  • its function-body shall be = delete, = default, or a compound-statement that contains only
    • null statements,
    • static_assert-declarations
    • typedef declarations and alias-declarations that do not define classes or enumerations,
    • using-declarations,
    • using-directives,
    • and exactly one return statement;"

I do not think there is any bug here, and especially nothing related to lambdas as mentioned in an earlier answer: variables simply cannot be declared inside of a constexpr function.

Sign up to request clarification or add additional context in comments.

6 Comments

But according to this site (i don't have a copy of the C++11 standard) pointer type is a literal type.
@milleniumbug: (sorry, I am editing this comment because the previous version was badly formatted): from what I understand, the standard defines nullptr as the only pointer-literal (2.14.7) "The pointer literal is the keyword nullptr. [...]"
btw you can download the working draft of the standard from this link: open-std.org/JTC1/SC22/WG21/docs/papers/2012/n3485.pdf
Oh well - A literal constant expression is a prvalue core constant expression of literal type, but not pointer type (after conversions as required by the context). (5.19.4)
BTW thanks for the link - this is even newer than what I found several months ago. I thought after the C++11 has been released, there won't be any updates. Silly me ;)
|
7

According to N3376 working draft of the standard section 5.19 [expr.const]:

Certain contexts require expressions that satisfy additional requirements as detailed in this sub-clause; other contexts have different semantics depending on whether or not an expression satisfies these requirements. Expressions that satisfy these requirements are called constant expressions. [ Note: Constant expressions can be evaluated during translation.— end note ]

It goes on to say:

A conditional-expression is a core constant expression unless it involves one of the following as a potentially evaluated subexpression (3.2), but subexpressions of logical AND (5.14), logical OR (5.15), and conditional (5.16) operations that are not evaluated are not considered [ Note: An overloaded operator invokes a function.— end note ]:

Which lists under it:

— a lambda-expression (5.1.2);

So while I don't know enough standardese, I believe this says that a constexpr shouldn't have a lambda expression inside.

3 Comments

I believe "constant expression" and "constexpr functions" are two different things. The requirements for a "constexpr function" in the standard are listed under 7.1.5 (point 3), and this doesn't mention that the body of a "constexpr function" must be a "constant expression". The latter probably allows for more than the former (but I admit I do not speak standardese either).
@AndyProwl I am unsure because all the example code from the section uses constexpr as a measure of "okay" or "not". I did end up looking further into it though and it lists the requirements for a constexpr function at around section 7.1.5 point 3 like you say so.
I believe it does so to show that a "constexpr variable" can be initialized by invoking a "constexpr function" and that makes it a valid "constant expression", but that does not specify what can and cannot be part of the body of a "constexpr function"
5

The error message gcc gave you was precise and correct:

error: call to non-constexpr function 'addition()::
             <lambda(int,int)>::
             operator int (*)(int, int)() const'

I've reformatted it a bit and added emphasis. By coercing the lambda to a function pointer, you're implicitly calling the automatically-created conversion function from lambda to pointer to function of type "auto (int, int)->int", which is not a constexpr function because the automatically-created conversion function is not declared constexpr (and the standard doesn't require it to be).

2 Comments

"It's too late" - doesn't matter. The more answers, the better for me ;) Have an upvote.
Now that I see it, this is completely obvious, but wasn't when I was asking. Probably I saw here operator int (int, int)() const instead and was confused. What You See Is Not Always What You Want To See indeed.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.