5

Consider the following code:

constexpr const int A = 42;

const int &B = A;
static_assert(&A == &B, "Bug");

constexpr const int &C = B;
static_assert(&A == &C, "Bug");

int main() { return 0; }

It is perfectly accepted by clang version 3.3, whereas g++ (SUSE Linux) 4.8.1 20130909 [gcc-4_8-branch revision 202388 refuses it with:

bug2.cpp:5:1: error: non-constant condition for static assertion
 static_assert(&A == &B, "Bug");
 ^
bug2.cpp:5:1: error: the value of ‘B’ is not usable in a constant expression
bug2.cpp:2:12: note: ‘B’ was not declared ‘constexpr’
 const int &B = A;
            ^

It seems to me that GCC is correct (whereas I certainly would prefer clang behavior). Trying to read the standard I realized that I'm not enough of a language lawyer to decide. Can anyone confirm that?

hivert
  • 10,579
  • 3
  • 31
  • 56
  • 1
    clang 3.5 generates an error on this code, [live](http://coliru.stacked-crooked.com/a/d83b4749715ad239). – Shafik Yaghmour Mar 05 '14 at 14:37
  • 1
    The clang project evolves very fast, 3.3 is already outdated. – user703016 Mar 05 '14 at 14:40
  • Do you actually want to *use* such code somewhere? Just wondering. – n. m. could be an AI Mar 05 '14 at 14:40
  • Not as such of course. If you want some background see my answer on http://stackoverflow.com/questions/22178366/array-of-class-element-as-a-static-constexpr-member – hivert Mar 05 '14 at 14:43
  • Changing `A` to static makes this work for the first case, the standard is a little vague on this, at least as far as I can tell. – Shafik Yaghmour Mar 05 '14 at 14:51
  • 1
    FYI this might have something to do with the change of rules for `constexpr` expressions between C++11 and C++14; C++14 introduced relaxed rules for `constexpr` expressions, which allow to *mutate* state in a `constexpr` function, and as a result `constexpr` in C++14 does not imply `const` (despite the name, which is awkward). – Matthieu M. Mar 05 '14 at 14:53
  • @MatthieuM. the closest I can find in the draft C++11 standard is this: `A reference constant expression is an lvalue core constant expression that designates an object with static storage duration or a function.` from `5.19 p 4` It does not tell much more than that but seems consistent with my previous observation. – Shafik Yaghmour Mar 05 '14 at 14:57
  • clang accepts your updated code but `gcc` does not, this looks like a a `gcc` bug to me and that makes more sense too. – Shafik Yaghmour Mar 05 '14 at 15:13

2 Answers2

3

the value of ‘B’ is not usable in a constant expression is incorrect. You are not performing an lvalue-to-rvalue conversion on B, which is the usual meaning of "value;" you are only taking its address. The only relevant constant-expression rule forbids:

an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization, initialized with a constant expression;

However B does have a preceding initialization to a reference constant expression.

A reference constant expression is an lvalue core constant expression that designates an object with static storage duration or a function.

In this case, B's initializer is A, which is as constant as you can get. A and B statically refer to the same object.

So, this is a GCC bug.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
1

Without being a language lawyer, you cannot use a reference as a constant expression in general, only values (if there are exceptions please correct me). In this sense, the closest valid to your code would be:

constexpr int A = 42;
constexpr int B = A;

static_assert(A == B, "Bug");

However, in my case, both Clang 3.3 and GCC 4.8.1 give errors on your code as expected.

EDIT Apparently my knowledge is still a bit narrow, sorry. There are probably exceptions depending on linkage, for instance the code is accepted (live) if the statements are global, outside main().

iavr
  • 7,547
  • 1
  • 18
  • 53
  • 2
    I don't think you are right. Just add `constexpr` to the line `const int &B = A;` and my code seems to be correct. – hivert Mar 05 '14 at 14:51
  • @hivert See [live](http://coliru.stacked-crooked.com/a/1ec66a890bc628fd) - am I missing something? – iavr Mar 05 '14 at 14:54
  • I don't know. This is completely different that my computer behavior. – hivert Mar 05 '14 at 14:58
  • @hivert See my edit, code compiles when statements are global, which you did not specify in your question. – iavr Mar 05 '14 at 15:06
  • @iavr yes that would make them static, which I commented on in the question. – Shafik Yaghmour Mar 05 '14 at 15:06
  • @iavr : I didn't realize: Of course the address of a *local* variable can't be computed at compile time. – hivert Mar 05 '14 at 15:08
  • @hivert Which doesn't mean that you can't use them in a constant expression: `&a == &b` where `a` and `b` are local objects is a core constant expression. – jrok Mar 05 '14 at 15:33
  • @jrok : How is this possible ? If a is a local variable `&a` is only know when you launch the function. – hivert Mar 05 '14 at 15:49
  • 2
    @hivert Intuitively, because addresses of two distinct objects cannot be equal and we know that at compile time. But see [here](http://stackoverflow.com/questions/18352385/is-taking-the-address-of-a-local-variable-a-constant-expression-in-c11) for a standardese discussion. – jrok Mar 05 '14 at 15:51