2

Say I want to build a graph at compile time, using some algorithm, and then count how many nodes ended up in the graph. This seems like an ideal situation for constexpr, rather than template metaprogramming, since the goal is a computation that yields a value, rather than really being about types. I have some code that works, but the feature is so new I'm afraid the compilers are being lenient, and I could interpret part of the standard as saying I can't do this.

#include <iostream> struct A { int x; constexpr A(int i) noexcept : x{i} {} }; struct B { A& a; constexpr B(A& a) noexcept : a{a} {} }; constexpr int foo() { A a{55}; B b{a}; return b.a.x; } template<int N> void output() { std::cout << N << std::endl; } int main() { // to be absolutely sure compile time eval'd, // pass as template arg constexpr auto b = foo(); output<b>(); } 

Both the a and b instances are created at compile time, and they have the same lifetime so this should be "safe". But a is a non-static object, and this part of the standard seems to say that's not allowed:

An entity is a permitted result of a constant expression if it is an object with static storage duration that is either not a temporary object or is a temporary object whose value satisfies the above constraints, or it is a function.

So can I or can't I? GCC and clang are both fine with it.

8
  • The code would be unproblematic in general because the result isn't used at compile time. A constexpr function does not have to produce a compile time constant. It can do that when supplied with compile time constant arguments (if any). Not sure about your case though. But the presented code doesn't exercise the compile time constant-ness. Commented Jul 30, 2015 at 22:23
  • @Jarod42: oversight, now it's changed, and same result. Commented Jul 30, 2015 at 22:27
  • @Cheersandhth.-Alf: I've added constexpr to the main declaration of 'b' to force it to be compile time. Commented Jul 30, 2015 at 22:27
  • Whereas A a{55}; may be constexpr, B b{a}; cannot be (with clang error message note: reference to 'a' is not a constant expression) Demo Commented Jul 30, 2015 at 23:05
  • Also see how to initialize a constexpr reference Commented Jul 30, 2015 at 23:58

3 Answers 3

5

Yes, your example is conforming.

The special thing about C++14 relaxed constexpr is that intermediate results inside the evaluation of a constant expression do not themselves need to be constant expressions.

return applies an lvalue-to-rvalue conversion to b.a.x, because the function returns by value, and b.a.x is:

a non-volatile glvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of e

(N4527 §5.20/2.7.4)

If you tried to save a (dangling) reference to b.a.x, that would be a problem. That would not be a "permitted result of a constant expression" per your quote.

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

2 Comments

Your answer makes the most sense but appears to contradict the others :p
@JosephGarvin I don't think the others understood the question or realized that your example is working.
0

In a nutshell, you can't pass non-static/temporary values as references at compile time. You can pass static/global values as constexpr references. But anything else is simply not available at compile time.

constexpr void foo() { int a; // run-time value. ... } 

An obvious solution would be to pass by value. It's happening at compile time, so you might not get the usual optimizations, but it's also happening at compile time.

http://ideone.com/J7mVj5

#include <iostream> struct A { int x; constexpr A(int i) noexcept : x{i} {} }; struct B { A a; constexpr B(A a) noexcept : a{a} {} }; constexpr int foo() { B b{55}; return b.a.x; } template<int N> void output() { std::cout << N << std::endl; } int main() { // to be absolutely sure compile time eval'd, // pass as template arg constexpr auto b = foo(); output<b>(); } 

See also http://ideone.com/tw4jzG

1 Comment

So Potatoeswatter is mistaken?
0

In your example:

B b{a}; 

b is not a constexpr variable the draft C++14 section 7.1.5 [dcl.constexpr]p5 which says:

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). Otherwise, or if a constexpr specifier is used in a reference declaration, every fullexpression that appears in its initializer shall be a constant expression. [ Note: Each implicit conversion used in converting the initializer expressions and each constructor call used for the initialization is part of such a full-expression. —end note ]

does not apply but if we modify your example slightly:

int main() { constexpr auto b = foo(); A a1(42) ; constexpr B b1( a1 ) ; } 

and introduce b1 which is a constexpr variable then this will not work (see it live), clang says:

error: 'B{a1}' is not a constant expression constexpr B b1( a1 ) ; ^ 

if we further modify the example from above as follows though:

static A a1(42) ; constexpr B b1( a1 ) ; 

it will work. A constexpr function is available for use in a constexpr but if it does not satisfy the requirements it will not yield a constant expression.

4 Comments

A static variable can't be initialized in a constant expression. It would be a side effect. See §7.1.5/3.4.4
@Potatoswatter I did not say it could, my live example does not do that, I added more context to my code to clarify this.
I don't see how this addresses the question, which is whether my snippet is valid. You seem to be presenting a different snippet which is not. You say b{a} is not constexpr, but my question is whether I'm allowed to have that within a constexpr function, which I'm not sure is the same (maybe it is?).
@ShafikYaghmour The last snippet in this answer can almost never work, and it cannot appear inside a constexpr function.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.