9

Example:

int main(int argc, char**)
{
    constexpr int a = argc * 0;
    (void)a;
    constexpr int b = argc - argc;
    (void)b;
    return 0;
}

argc is not a constant expression, but the compiler is still able to compute the results of a and b in compile time (i.e. 0) in both cases.

g++ accepts the code above, while clang and MSVC14 reject it.

Does the standard allows the compiler being as smart as g++ with regard to constexpr?

Mike Kinghan
  • 55,740
  • 12
  • 153
  • 182
Jamboree
  • 5,139
  • 2
  • 16
  • 36
  • 1
    I think both of these should be rejected due to [expr.const]/2: `argc` is not a core constant expression because it involves lvalue-to-rvalue conversion of a variable that wasn't initialized with a constant expression. – M.M Oct 03 '15 at 09:37
  • 1
    Now that I had a chance to think about it this looks similar to [int a=1, is a || 1 a constant expression?](http://stackoverflow.com/q/31526278/1708801) – Shafik Yaghmour Oct 03 '15 at 19:06

2 Answers2

8

Neither argc * 0 nor argc - argc are constant expressions, the lvalue-to-rvalue conversion is allowed in certain cases none of them apply here. If we look at the draft C++11 standard section 5.19 [expr.const] it lays out the exceptions. It says:

A conditional-expression is a core constant expression unless it involves one of the following as a potentially evaluated subexpression [...]

and has several bullets including the following on lvalue-to-rvalue conversion:

an lvalue-to-rvalue conversion (4.1) unless it is applied to

  • a glvalue of integral or enumeration type that refers to a non-volatile const object with a preceding initialization, initialized with a constant expression, or

  • a glvalue of literal type that refers to a non-volatile object defined with constexpr, or that refers to a sub-object of such an object, or

  • a glvalue of literal type that refers to a non-volatile temporary object whose lifetime has not ended, initialized with a constant expression;

It is interesting to note that gcc does not accept the following code (see it live):

constexpr int a = argc * 2;

So it looks like gcc is saying I know the result will be zero and therefore it performs constant folding and it does not need to perform the lvalue-to-rvalue conversion of argc to determine the result.

Unfortunately I don't see any provisions in section 5.19 that allows this kind of short-circuiting. This looks very similar to case in int a=1, is a || 1 a constant expression? which has a bug report but no one from the gcc team has replied to that one. I added a comment to that bug report indicating this seems related.

Mark's comment below indicates this is a bug:

There is a whole c++-delayed-folding branch on which some gcc developers are working, which will delay a number of optimizations and might fix this. It is important for other reasons, rejecting the code in this question is very low priority

Do constant expressions strictly require every sub-expression to be a constant expression? No, for example from 5.19 it says: (emphasis mine)

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 [...]

So the following is a constant expression:

constexpr int a = false && argc * 0;

Because argc * 0 is not evaluated since && evaluates left-to-right and short-circuits.

Community
  • 1
  • 1
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • There is a whole c++-delayed-folding branch on which some gcc developers are working, which will delay a number of optimizations and might fix this. It is important for other reasons, rejecting the code in this question is very low priority. – Marc Glisse Oct 04 '15 at 18:39
  • @downvote please explain what is wrong with my answer? – Shafik Yaghmour Oct 04 '15 at 19:03
  • @MarcGlisse thank you for the information, I can not think of a lot of scenarios where this would lead a serious issue, aside from portability type issues. Are there any mailings you could point me to, I am curious to understand the issue better. – Shafik Yaghmour Oct 04 '15 at 19:09
  • @MarcGlisse I guess [this other issue](http://stackoverflow.com/q/31526278/1708801) is due to the same cause? – Shafik Yaghmour Oct 04 '15 at 19:11
0

You can take a look here: constexpr at cppreference.com

The baseline is that a constexpr should be something that can be determined unambiguously at compile-time so that the compiler can substitute its value in the expression is evaluated.

I think the problem is the argc is not a constexpr. Is not something the compiler knows. In fact you can execute your main with a different number of arguments. If gcc accepts it it may be a bug or there may be some subtle thing involved I don't get.

OmnipotentEntity
  • 16,531
  • 6
  • 62
  • 96
Triskeldeian
  • 590
  • 3
  • 18
  • 1
    "something that can be determined unambiguously at compile-time", both `a` and `b` can be determined at compile time. – Emil Laine Oct 03 '15 at 09:08
  • @zenith unfortunately the rules do not allow for bypassing the lvalue-to-rvalue conversion of `argc` and therefore it makes it a non-constant expression. – Shafik Yaghmour Oct 04 '15 at 00:24