Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using argc in a constexpr, is it strictly required that any sub-expression involved be a constant expression?

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?

like image 634
Jamboree Avatar asked Oct 03 '15 07:10

Jamboree


People also ask

Is constexpr always const?

A constexpr variable must be initialized at compile time. All constexpr variables are const . A variable can be declared with constexpr , when it has a literal type and is initialized. If the initialization is performed by a constructor, the constructor must be declared as constexpr .

Why should I use constexpr?

The primary usage of constexpr is to declare intent. If an entity isn't marked as constexpr - it was never intended to be used in a constant-expression; and even if it is, we rely on the compiler to diagnose such context (because it disregards our intent).

Can a constexpr function throw an exception?

Even though try blocks and inline assembly are allowed in constexpr functions, throwing exceptions or executing the assembly is still disallowed in a constant expression.

Can constexpr variable be changed?

Both const and constexpr mean that their values can't be changed after their initialization. So for example: const int x1=10; constexpr int x2=10; x1=20; // ERROR. Variable 'x1' can't be changed.


1 Answers

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.

like image 58
Shafik Yaghmour Avatar answered Oct 13 '22 12:10

Shafik Yaghmour