lmi
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [lmi] static constexpr


From: Greg Chicares
Subject: Re: [lmi] static constexpr
Date: Wed, 16 Jun 2021 22:20:45 +0000
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.11.0

On 6/16/21 4:42 PM, Vadim Zeitlin wrote:
> On Wed, 16 Jun 2021 14:49:24 +0000 Greg Chicares <gchicares@sbcglobal.net> 
> wrote:
[...]
> rint() is not constexpr in C++20 (this might change in
> C++23, but doesn't help right now) and I don't know how to replace it with
> something that could be evaluated at compile-time.

Yup. I think we should just wait for it to become constexpr in a
future version. In 'round_to.hpp', IIRC, we once had a simulated
rint(), but I would be disinclined to disinter it even if it could
be made constexpr (it probably couldn't, anyway).

IIRC, in class currency, I used std::rint() only because it achieves
better speed than its close relatives, by not setting and unsetting
the rounding-direction bits. It seems weird to constexpr-ify a
function whose behavior depends on run-time state. However, when we
use rint(), we generally don't intend to demand rounding according to
the current state; instead, most likely we're declaring that whatever
the current state may be, it's good enough. It's much like the situation
with floating literals: if they're interpreted at compile time, they
might not exactly equal what a run-time interpretation would yield;
but no one really complains about that, so I guess no one will complain
whether std::rint(1.5) returns one versus two.

> GC>   view_ex.cpp://  static constexpr std::string unnameable{"Hastur"};
[...]
> GC> Maybe it'll work with a future gcc version.
> 
>  There are indeed plans to do it, AFAIR, but right now you have to write
> your own constexpr string if you need it (as CTRE does).
> 
> GC> Until then, today's code:
> GC>     static std::string const unnameable{"Hastur"};
> GC> is plenty good enough.
> 
>  Yes, but it could still be replaced with constexpr string_view

I'd be disinclined to write the most optimal C++20 equivalent
today, only to replace it with constexpr later.

> GC>   stratified_algorithms.hpp:///   static constexpr T zero {};
> GC>   stratified_algorithms.hpp:    static constexpr T zero {};
> GC>   stratified_algorithms.hpp:    static constexpr T zero {};
> GC>   stratified_algorithms.hpp:    static constexpr T zero {};
> GC>   stratified_algorithms.hpp:    static constexpr T zero {};
> GC>   stratified_algorithms.hpp:    static constexpr T zero {};
> GC> 
> GC> Those six cases are documented thus:
> GC> 
> GC>   /// Implementation note: Many function templates in this header
> GC>   /// require a zero of the appropriate type T, and accordingly define
> GC>   /// a local variable
> GC>   ///   static constexpr T zero {};
> GC>   /// in case this zero is expensive to construct. It is constructed
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Now there's something curious: the comment emphasized above says
that the purpose is to guard against a potentially expensive ctor.
IIRC, the motivation was that at one time class currency's ctor was
potentially expensive; but at that time, it couldn't have been
constexpr. Thus, it would be wrong to remove 'static' here without
reconsidering and rewriting the comment.

> GC>   /// as "T{}" rather than "T(0)" because the latter uses an explicit
> GC>   /// integer argument, which may require a converting constructor
> GC>   /// (for example, with class currency).
> GC> 
> GC> so 'static constexpr' seems appropriate: the constant may be
> GC> expensive to construct, so we want to construct it OAOO and
> GC> store its value.
> 
>  Sorry, this is the (only) one change that I disagree with. Making it
> static doesn't help help at all with constructing it once compared to
> making it just constexpr: in the latter case it's constructed once at
> compile-time, in the latter case it's constructed at compile-time but also
> appears in the executable during run-time, which is needless. I.e.
> "constexpr" alone is sufficient to construct it OAOO at compile-time and
> while "static" would indeed be needed to store its value, we just don't
> need to do this at all.

That's my question: is it truly constructed OAOO? [By the end of
this post, I think I've constructed a valid argument that the
initializing value is guaranteed to be computed at compile time,
which isn't precisely the same thing, but is good enough for me.]

Consider the sample program in this post:
  https://stackoverflow.com/a/58328980
which I've compiled with pc-linux-gnu gcc-10.2, obtaining the
following output...

value \ address of const is               1 78e
value \ address of const is               2 72e
value \ address of const is               3 6ce

value \ address of static is              1 320
value \ address of static is              1 320
value \ address of static is              1 320

value \ address of static const is        1 310
value \ address of static const is        1 310
value \ address of static const is        1 310

value \ address of constexpr is           0 78e
value \ address of constexpr is           0 72e
value \ address of constexpr is           0 6ce

value \ address of static constexpr is    0 4a0
value \ address of static constexpr is    0 4a0
value \ address of static constexpr is    0 4a0

...which seems to suggest that a function-local 'constexpr' variable
is initialized every time the function is called, but if we make it
'static constexpr', then it's initialized OAOO.

If I'm mistaken and that doesn't demonstrate what I think it does,
then what else could actually be going on here? We might suppose
that the compiler computes and memo-izes the initializer, and then
initializes the 'constexpr' variable three times, with the same
initializer (precomputed at compile time) but a different address;
but I want to know that that's guaranteed. It wouldn't be unreasonable
to hope that the compiler would optimize it away, if only we didn't
defeat that optimization by taking the variable's address; but I'm
looking for a guaranteed answer, not a reasonable expectation.

With 'static constexpr', there's no "maybe": OAOO is guaranteed.
I think you're saying that 'constexpr' alone guarantees that the
initialization happens OAOO (unless we force that not to happen by
perverse use of operator&). But can that be proven?

Here's why I feel somewhat queasy:

  https://isocpp.org/blog/2013/12/constexpr
| constexpr guarantees compile-time evaluation *is possible if*
| operating on a compile-time value, and that compile-time
| evaluation *will happen if* a compile-time result is needed.

so in the lmi code we're discussing:
  constexpr T zero {};
we're guaranteed that compile-time evaluation is possible
(because it doesn't depend on any non-compile-time value),
but are we guaranteed that compile-time evaluation will
occur?

Is the answer that this:
  constexpr T zero {};
is a "constexpr context"? so that the "*will happen if*" clause
above necessarily comes into effect? and so that, no matter what
initializer follows
  constexpr T zero ...
, if it compiles, then the initialization is *guaranteed* to
occur at compile time? Or, more properly speaking, that the
*initializing value* must be computed at compile time, which
would be enough to satisfy me? Then, in the stackoverflow
program quoted above,
  value \ address of constexpr is           0 78e
  value \ address of constexpr is           0 72e
  value \ address of constexpr is           0 6ce
we're guaranteed that the initializing value '0' is computed at
compile time, and if we (using operator&) force the compiler to
copy that memo-ized value to various addresses, it's conceptually
as simple as a register swap?

In that case, then, specifying 'static' with 'constexpr' has no
benefit (the initializing value is computed OAOO without 'static'),
and adding 'static' has only the harmful effect of inducing a
reasonable compiler to allocate an address for the variable,
which impedes optimization?


reply via email to

[Prev in Thread] Current Thread [Next in Thread]