Shouldn't the constexpr variables be treated as static by default?

4,917 views
Skip to first unread message

Mikhail Semenov

unread,
Mar 30, 2014, 4:55:58 PM3/30/14
to std-pr...@isocpp.org
There is a recommendation to define all constexpr variables as static.
Shouldn't they be defined like that by default, meaning wherever we write

constexpr double x = 5.1;

the variable x will be assumed  always to have  static storage duration?







Andrew Tomazos

unread,
Mar 30, 2014, 5:02:28 PM3/30/14
to std-pr...@isocpp.org
This would be a breaking change:

#include <cstdio>

void f(int y)
{
        if (y == 0)
                return;

        constexpr int x = 3;
        const int* p = &x;

        std::printf("%p\n", p);

        f(y-1);
}

int main()
{
        f(3);
}

prints:

0x7fff125fdad8
0x7fff125fdaa8
0x7fff125fda78

whereas with static constexpr it prints:

0x400714
0x400714
0x400714





--

---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.

Geoffrey Romer

unread,
Mar 30, 2014, 11:17:48 PM3/30/14
to std-pr...@isocpp.org

It sounds to me like you're saying that moving x from one part of the address space to another would constitute a breaking change. Are you really making that claim?

To put it another way, what in the current standard prevents that code from printing this?

0x400714
0x400714
0x400714

And under this proposed change, what would prevent that code from printing this?

0x7fff125fdad8
0x7fff125fdaa8
0x7fff125fda78

Thiago Macieira

unread,
Mar 30, 2014, 11:28:58 PM3/30/14
to std-pr...@isocpp.org
Em dom 30 mar 2014, às 20:17:48, Geoffrey Romer escreveu:
> To put it another way, what in the current standard prevents that code from
> printing this?
>
> 0x400714
> 0x400714
> 0x400714
>
> And under this proposed change, what would prevent that code from printing
> this?
>
> 0x7fff125fdad8
> 0x7fff125fdaa8
> 0x7fff125fda78

The point is not the address, but the fact that currently the variable has
automatic storage duration and changing it to have static storage would be a
huge change.

That said, I think the OP was not talking about constexpr variables inside
functions, but about global ones.

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358

Andrew Tomazos

unread,
Mar 31, 2014, 12:39:58 AM3/31/14
to std-pr...@isocpp.org
On Monday, March 31, 2014 5:17:48 AM UTC+2, Geoffrey Romer wrote:

It sounds to me like you're saying that moving x from one part of the address space to another would constitute a breaking change. Are you really making that claim?

The feature of interest is whether the three addresses are equal or distinct...
 

To put it another way, what in the current standard prevents that code from printing this?

0x400714
0x400714
0x400714

 [intro.object]/6 prevents this:

Two objects that are not bit-fields may have the same address if one is a subobject of the other, or if at least one is a base class subobject of zero size and they are of different types; otherwise, they shall have distinct addresses

With automatic storage duration, the three objects must have distinct addresses.  The three above are not distinct addresses.

And under this proposed change, what would prevent that code from printing this?

0x7fff125fdad8
0x7fff125fdaa8
0x7fff125fda78

With static storage duration, all names x refer to the same object, so all variables p must be equal.  The above three addresses are not equal.

Geoffrey Romer

unread,
Mar 31, 2014, 1:18:43 AM3/31/14
to std-pr...@isocpp.org

Ah, I hadn't noticed the addresses were varying in the static example. I grant that this could theoretically be a breaking change, but how much real code would be broken? The kinds of things you'd have to do to notice the difference seem like things that you'd normally never do with a constexpr variable.

Thiago Macieira

unread,
Mar 31, 2014, 2:27:34 AM3/31/14
to std-pr...@isocpp.org
Em dom 30 mar 2014, às 22:18:43, Geoffrey Romer escreveu:
> Ah, I hadn't noticed the addresses were varying in the static example. I
> grant that this could theoretically be a breaking change, but how much real
> code would be broken? The kinds of things you'd have to do to notice the
> difference seem like things that you'd normally never do with a constexpr
> variable.

I'd say that for anyone who caused the address to be taken, they really want
the current behaviour. So it will break a lot of code.

What's the benefit you were looking for?

Mikhail Semenov

unread,
Mar 31, 2014, 3:47:09 AM3/31/14
to std-pr...@isocpp.org
The rules for odr-used in lambda are rather complicated, for a start: we've got const, static const.
In general , it is recommended to write static constexpr anyway: why write two words instead of one?
Another point is that it is constant, no matter how you look at it, why should it be automatic?
 
I haven't written a proposal yet, but I meant all the contexts of defining constexpr variables:
top-level, member and local.

Richard Smith

unread,
Mar 31, 2014, 3:47:23 AM3/31/14
to std-pr...@isocpp.org
On Sun, Mar 30, 2014 at 11:27 PM, Thiago Macieira <thi...@macieira.org> wrote:
Em dom 30 mar 2014, às 22:18:43, Geoffrey Romer escreveu:
> Ah, I hadn't noticed the addresses were varying in the static example. I
> grant that this could theoretically be a breaking change, but how much real
> code would be broken? The kinds of things you'd have to do to notice the
> difference seem like things that you'd normally never do with a constexpr
> variable.

I'd say that for anyone who caused the address to be taken, they really want
the current behaviour. So it will break a lot of code.

Can you point to any code it would break? I'm having a hard time seeing why you would ever want an automatic storage duration constexpr variable.
 
What's the benefit you were looking for?

It'd fix this weirdness:

void f() {
  constexpr auto x = { 1, 2, 3 }; // ill-formed
  static constexpr auto y = { 1, 2, 3 }; // ok

  // or more obviously...
  constexpr auto &&a = 1; // ill-formed
  static constexpr auto &&b = 1; // ok
}

The problem in these cases is that a temporary is lifetime-extended to the lifetime of the variable (an array temporary in the first cases, and an int temporary in the last cases), and a constexpr reference can't bind to an automatic storage duration object.

It'd also remove the unnecessary stack usage in common cases like:

void g(const char*);
void h() {
  constexpr char foo[] = "some string literal goes here";
  g(foo);
}

Under the current language rules, the compiler is *required* to allocate stack space and copy that string literal onto the stack each time through this function (assuming it can't see the definition of 'g').

Richard Smith

unread,
Mar 31, 2014, 3:57:25 AM3/31/14
to std-pr...@isocpp.org
On Mon, Mar 31, 2014 at 12:47 AM, Mikhail Semenov <mikhailse...@gmail.com> wrote:
The rules for odr-used in lambda are rather complicated, for a start: we've got const, static const.
In general , it is recommended to write static constexpr anyway: why write two words instead of one?
Another point is that it is constant, no matter how you look at it, why should it be automatic?
 
I haven't written a proposal yet, but I meant all the contexts of defining constexpr variables:
top-level, member and local.

These are three very different changes.

For top-level variables, 'constexpr' already implies internal linkage (if the variable is not a reference and is not declared with 'extern'), because it gives it a const-qualified type. So I'm not sure that any change is really warranted here.

For member variables, 'constexpr' implying 'static' would eliminate a possible evolution route for the language (constexpr non-static data members, whatever that might mean). The win here seems marginal, and it would make code more difficult to read, because it would no longer be obvious which members of a class are static. (thread_local implying static is already pretty bad in this regard.) I'm not sure you'll find consensus for a change here.

For local variables (declared 'constexpr' and not 'extern'), there are several compelling advantages. This case seems easiest to argue, and there's even implementation experience -- Clang's default compilation mode treats large, local, constant-enough variables as if they were static. (Here, "constant enough" means basically: a const-qualified type, a constant-foldable initializer, a trivial destructor, and no mutable members.) To my knowledge, only contrived test cases have ever had problems with this behavior.

On Monday, March 31, 2014 7:27:34 AM UTC+1, Thiago Macieira wrote:
Em dom 30 mar 2014, às 22:18:43, Geoffrey Romer escreveu:
> Ah, I hadn't noticed the addresses were varying in the static example. I
> grant that this could theoretically be a breaking change, but how much real
> code would be broken? The kinds of things you'd have to do to notice the
> difference seem like things that you'd normally never do with a constexpr
> variable.

I'd say that for anyone who caused the address to be taken, they really want
the current behaviour. So it will break a lot of code.

What's the benefit you were looking for?
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
   Software Architect - Intel Open Source Technology Center
      PGP/GPG: 0x6EF45358; fingerprint:
      E067 918B B660 DBD1 105C  966C 33F5 F005 6EF4 5358

Mikhail Semenov

unread,
Mar 31, 2014, 4:49:34 AM3/31/14
to std-pr...@isocpp.org
I agree with the top level, that the values are already in the static segment. I think if change is to be made, it should be done for both member variables and local variables.
Otherwise, it will be confusing. After all it is recommended, that constexpr members should be static.
 
Anyway, my point is that static segment should cover all the cases of constexpr usage, no cherry-picking.
 


Paul A. Tessier

unread,
Mar 31, 2014, 5:48:13 AM3/31/14
to std-pr...@isocpp.org
--

---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
I've slammed my head into this a few time while using constexpr. The logical assumption is that it's static, and your examples point this out nicely.

Until reading this thread I hadn't realized they weren't static. Assuming you didn't want your variables to be static, why wouldn't you just use const for a local copy?

class A {
    // assumed to be static in a sane world
    constexpr auto cx = some_thing();
   
    // non-static copy in every object

    const auto x = cx;

};

Currently we have this conundrum:

class A {
    // non-static, why?
    constexpr auto cx = some_thing();
};

Which is internally translated to this:

// ok ...
static constexpr auto __hidden_internal_A_cx_ = some_thing();

class A {

    // great a hidden copy every instantiation
    constexpr auto cx = __hidden_internal_A_cx_;
};

Seems wasteful and limiting for no good reason I can think of. It also places odd restrictions on constexpr that you showed above in your examples. A constexpr variable must have static storage at some point, it's baked in when the program is compiled. Allowing it to be treated as non-static creates a weird set of corner cases.

Nevin Liber

unread,
Mar 31, 2014, 10:57:33 AM3/31/14
to std-pr...@isocpp.org
I'd like to separate the syntactical issue (whether or not to use 'static') from the semantic issues.

On 31 March 2014 02:57, Richard Smith <ric...@metafoo.co.uk> wrote:

For member variables, 'constexpr' implying 'static' would eliminate a possible evolution route for the language (constexpr non-static data members, whatever that might mean). The win here seems marginal, and it would make code more difficult to read, because it would no longer be obvious which members of a class are static. (thread_local implying static is already pretty bad in this regard.) I'm not sure you'll find consensus for a change here.

For this reason, I am against saying that constexpr variables are also implicitly static.  I don't want that anywhere, as it just requires developers to have more arcane knowledge to understand the code.

For local variables (declared 'constexpr' and not 'extern'), there are several compelling advantages. This case seems easiest to argue, and there's even implementation experience -- Clang's default compilation mode treats large, local, constant-enough variables as if they were static. (Here, "constant enough" means basically: a const-qualified type, a constant-foldable initializer, a trivial destructor, and no mutable members.) To my knowledge, only contrived test cases have ever had problems with this behavior.

I am not against requiring constexpr local variables to be explicitly marked static.
--
 Nevin ":-)" Liber  <mailto:ne...@eviloverlord.com(847) 691-1404

Andrew Tomazos

unread,
Mar 31, 2014, 11:02:01 AM3/31/14
to std-pr...@isocpp.org
On Monday, March 31, 2014 7:18:43 AM UTC+2, Geoffrey Romer wrote:

Ah, I hadn't noticed the addresses were varying in the static example. I grant that this could theoretically be a breaking change, but how much real code would be broken? The kinds of things you'd have to do to notice the difference seem like things that you'd normally never do with a constexpr variable.

A breaking change means that one or more well-formed C++ programs would have to exhibit different behavior under the change.  My example shows one such program, proving it is a breaking change.

The committee can and does make breaking changes if they feel the number of real world programs that would be so broken is small enough compared to the benefit of the change.

How many real-world programs would be broken by this change is a good question.  I'm not really sure - however if the variable x in question was a complex user-defined literal type, and not just an int, the p address would be the this pointer within its member functions.  In this light, are we so sure the three objects sharing the same storage couldn't cause real world problems?  What if they have mutable data members?

In general, constexpr asserts that an entity can be used in certain ways at compile-time, it doesn't mean that the entity cannot also be used at run-time as well.  Arguments can and have been made that we should also be able to assert that an entity may not be used at run-time, but this isn't what constexpr does.

David Krauss

unread,
Apr 1, 2014, 1:46:27 PM4/1/14
to std-pr...@isocpp.org

On 2014–03–31, at 5:48 PM, Paul A. Tessier <pher...@gmail.com> wrote:

A constexpr variable must have static storage at some point, it's baked in when the program is compiled. Allowing it to be treated as non-static creates a weird set of corner cases.

I’m pretty sure you’re allowed to embed runtime data in a constexpr object using mutable.

This thread does touch on the behavior of extern constexpr, which I believe to be a defect… need to write a paper…

Paul A. Tessier

unread,
Apr 1, 2014, 2:11:01 PM4/1/14
to std-pr...@isocpp.org
You still'd get a copy from the immutable static version. The copy just happens to have a mutable field, which in my opinion isn't really constexpr object as much as it is a const object with a mutable field.

Were basically stuck with poor semantics and too much variability on something that has hard defined limits. All constexpr objects have static storage, irregardless of extra features that confound their use. Therefore I believe the syntax should be cleaned to show this, but we're probably too far into the weeds now to ever fix this in a clear and concise manner. Making all constexpr objects static by default is at least a step in the right direction.

Mikhail Semenov

unread,
Apr 1, 2014, 3:00:10 PM4/1/14
to std-pr...@isocpp.org
I see three issues here:
(1) Is there a strong case for mutable fields in a constexpr object?
(2) The advantage of having constexpr variables in a static segment is:
     (a) in a lambda expression, you don't have to capture them because they are static (automatically), no matter on which level they are defined;
     (b) if  they are used as class members, you don't have to write special assignment operators for class objects;
(despite what some of you think, both these issues make it easier to use them by novices; as I mentioned before "odr-used" definition is too complicated; missing the word static leads to unexpected results; the language looks cleaner if static is implied);
(3) There is an issue is with the other semantics of the word "static" (this word, on its own, is confusing enough): should constexpr variables be hidden from external usage? (I actually did not argue this case, but maybe it should be considered as well).

Actually, in C++, there is a general approach not to use the word  static quite often: it's pretty bad with member variables (leads to difficult-to-find errors); on top level, it can be avoided by using anonymous namespaces. It looks like it's going to be used often with constexpr.

Richard Smith

unread,
Apr 1, 2014, 3:05:29 PM4/1/14
to std-pr...@isocpp.org
What do you think is wrong with 'extern constexpr'? It seems practically useful and consistent with the other language rules... 

Richard Smith

unread,
Apr 1, 2014, 3:10:51 PM4/1/14
to std-pr...@isocpp.org
On Tue, Apr 1, 2014 at 12:00 PM, Mikhail Semenov <mikhailse...@gmail.com> wrote:
I see three issues here:
(1) Is there a strong case for mutable fields in a constexpr object?

Yes.

template<typename T> struct static_init { mutable T value; };

// this object is guaranteed to have static initialization and trivial destruction (safe for access before main() and during program shutdown), not const
constexpr static_init<my_type> my_object = { ... };

(2) The advantage of having constexpr variables in a static segment is:
     (a) in a lambda expression, you don't have to capture them because they are static (automatically), no matter on which level they are defined;
     (b) if  they are used as class members, you don't have to write special assignment operators for class objects;

I don't follow this. If they're class members, they're already required to be static.

Another is, static storage duration constexpr variables can refer to lifetime-extended temporaries (and thus can be std::initializer_lists); automatic (or thread) storage duration constexpr variables cannot.
 
(despite what some of you think, both these issues make it easier to use them by novices; as I mentioned before "odr-used" definition is too complicated; missing the word static leads to unexpected results; the language looks cleaner if static is implied);
(3) There is an issue is with the other semantics of the word "static" (this word, on its own, is confusing enough): should constexpr variables be hidden from external usage? (I actually did not argue this case, but maybe it should be considered as well).

They already are (except in the corner case of a constexpr reference), because const type implies internal linkage.

David Krauss

unread,
Apr 1, 2014, 3:35:33 PM4/1/14
to std-pr...@isocpp.org

On 2014–04–02, at 3:05 AM, Richard Smith <ric...@metafoo.co.uk> wrote:

What do you think is wrong with 'extern constexpr'? It seems practically useful and consistent with the other language rules… 

I think we already discussed this? Perhaps it was Ville. I’ll summarize.

Internal linkage, which is the default, does not allow safe usage in inline function bodies, particularly for objects of class type.

External linkage requires definition in exactly one TU. Successfully using extern constexpr requires non-constexpr declarations in all other TUs.

There needs to be a way to declare constexpr class objects without an inline accessor function. The C++11 standard already has a couple of them, and they cannot safely be used in inline functions. As a result, there must be a ton of ill-formed code out there, with ODR optional diagnosis.

Perhaps extern constexpr isn’t broken, because you can still access the object from another TU without constexpr, but it seems pretty clear to me that the status quo is far from optimal.

The paper would essentially point out how broken things are, and how little could break by changing things. There’s no need, for example, to disallow non-constexpr extern declarations from linking to an extern constexpr variable with identical definitions in several TUs. So all valid usages of extern constexpr would remain so.

Message has been deleted

Mikhail Semenov

unread,
Apr 1, 2014, 3:59:08 PM4/1/14
to std-pr...@isocpp.org
>I don't follow this. If they're class members, they're already required to be static.

I only meant "members in a class", which may belong to the class or an object of this class:

struct A
{
      constexpr int x = 5;
      int y;
};


vs

struct A1
{
      static constexpr int x = 5;
      int y;
};

You have to write a special assignment for A, but not for A1.
________________________________________________________________
As for lambda:

#include <iostream>

const int& f(int a, const int& x) { return x;}
int f(char a, int x) { return x;}

int main() 
{
    constexpr int x = 17; // it will be OK if static is used here!
    auto g = [](auto a) 
    {
        const int i = f(a,x);
        std::cout << "i:" << i << std::endl;
    };
    g('a'); // okay: does not capture x
    g(17); // error: captures , but ok with static
};
_____________________________________________________________

>They already are (except in the corner case of a constexpr reference), because const type implies internal linkage.

Yes, they are. I mentioned that I did not consider this case in my initial message.






David Krauss

unread,
Apr 1, 2014, 4:20:18 PM4/1/14
to std-pr...@isocpp.org
On 2014–04–02, at 3:10 AM, Richard Smith <ric...@metafoo.co.uk> wrote:

I don't follow this. If they're class members, they're already required to be static.

Hmm, I didn’t know this. Isn’t it over-restrictive?

As with the other cases in this thread, I would expect nonstatic constexpr to maybe produce a warning in the event of no mutable members, but otherwise the default meaning would seem to be fine, except that a mem-initializer or aggregate initialization should be forbidden. A brace-or-equal-initializer would be required.

A use case would be a subobject with some tags or flags interpreted by template code (or, say, controlling conditional branches), along with a variable payload. The workaround would be to use instances of std::integral_constant with implicit conversion, but that’s not nearly as elegant. The tags and flags would be runtime variant when inspected through a non-constexpr path.

(despite what some of you think, both these issues make it easier to use them by novices; as I mentioned before "odr-used" definition is too complicated; missing the word static leads to unexpected results; the language looks cleaner if static is implied);
(3) There is an issue is with the other semantics of the word "static" (this word, on its own, is confusing enough): should constexpr variables be hidden from external usage? (I actually did not argue this case, but maybe it should be considered as well).

They already are (except in the corner case of a constexpr reference), because const type implies internal linkage.

Well, unless you say extern. But I don’t see how internal (static) is cleaner; quite the opposite. Usually constants don’t vary between TUs, and the ODR is much simpler for external linkage.

Richard Smith

unread,
Apr 1, 2014, 6:45:06 PM4/1/14
to std-pr...@isocpp.org
I think I see what you're saying:

  If you accidentally odr-use a global constexpr variable in an inline function, your code will be ill-formed (NDR). And for such an object of class type, you'll odr-use it every time you call a member function on it.

We've always had this issue for global const variables, but I think you're saying it's more significant for constexpr because global constexpr variables are likely to be more common. And you'd like to fix this by allowing definitions of 'extern constexpr' variables in multiple TUs -- it's not that there's something wrong with 'extern constexpr' itself, it's that you have another feature that you'd like to model using those tokens.

Yes, I remember discussing this before (sorry for forgetting!). IIRC I suggested 'inline' is a keyword which already means 'merged under the ODR', but you found "inline constexpr T var = init;" unsatisfying.

FWIW, you can get the behavior you're looking for by replacing

  extern constexpr T var = init;

with

  template<typename = void>
  struct Whatever {
    static constexpr T value = init;
  };
  template<typename U> T Whatever<U>::value;
  constexpr T &var = Whatever<>::value;

but I expect you'll point out that that too is a very unsatisfying solution, and I agree.

Richard Smith

unread,
Apr 1, 2014, 6:48:41 PM4/1/14
to std-pr...@isocpp.org
On Tue, Apr 1, 2014 at 12:59 PM, Mikhail Semenov <mikhailse...@gmail.com> wrote:
>I don't follow this. If they're class members, they're already required to be static.

I only meant "members in a class", which may belong to the class or an object of this class:

struct A
{
      constexpr int x = 5;
      int y;
};

This code is ill-formed. A non-static data member cannot be constexpr.
 
vs

struct A1
{
      static constexpr int x = 5;
      int y;
};

You have to write a special assignment for A, but not for A1.
________________________________________________________________
As for lambda:

#include <iostream>

const int& f(int a, const int& x) { return x;}
int f(char a, int x) { return x;}

int main() 
{
    constexpr int x = 17; // it will be OK if static is used here!
    auto g = [](auto a) 
    {
        const int i = f(a,x);
        std::cout << "i:" << i << std::endl;
    };
    g('a'); // okay: does not capture x
    g(17); // error: captures , but ok with static
};

Yes, I agree that non-static (non-thread_local) local constexpr variables are probably always a mistake.

_____________________________________________________________

>They already are (except in the corner case of a constexpr reference), because const type implies internal linkage.

Yes, they are. I mentioned that I did not consider this case in my initial message.

Show trimmed content





Mikhail Semenov

unread,
Apr 2, 2014, 3:58:13 AM4/2/14
to std-pr...@isocpp.org
Actually, I hadn't  formulated my first question correctly:
Is there a strong case for mutable fields in a constexpr object which is created on the stack? 
Reply all
Reply to author
Forward
0 new messages