Topic: Why this behaviour for literal types?


Author: =?ISO-8859-2?Q?Andrzej_Krzemie=F1ski?=<akrzemi1@gmail.com>
Date: Wed, 14 Mar 2012 13:33:54 -0700 (PDT)
Raw View
Hi,
This is my literal type:

   struct C
   {
     double m;
     constexpr C( double m ) : m{m} {};
     constexpr double get() const { return m; };
   };

Therefore the following code works:

   constexpr C c = C{5.};
   constexpr double k = c.get();

But the following does not:

   const C cc = C{5.};  // but not constexpr
   constexpr double kk = cc.get();

Why is this so? Object cc is subject to const initialization anyway. It is possible to make it work only by a seemingly irrelevant change in declaration (add constexpr). What was the reason for disallowing this "implicit generation of compile-time constants"?

Regards,
&rzej


--
[ comp.std.c++ is moderated.  To submit articles, try posting with your ]
[ newsreader.  If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]




Author: =?UTF-8?B?RGFuaWVsIEtyw7xnbGVy?=<daniel.kruegler@googlemail.com>
Date: Thu, 15 Mar 2012 11:33:34 -0700 (PDT)
Raw View
Am 14.03.2012 21:33, schrieb Andrzej Krzemie  ski:
>  Hi,
>  This is my literal type:
>
>      struct C
>      {
>        double m;
>        constexpr C( double m ) : m{m} {};
>        constexpr double get() const { return m; };
>      };
>
>  Therefore the following code works:
>
>      constexpr C c = C{5.};
>      constexpr double k = c.get();

Correct.

>  But the following does not:
>
>      const C cc = C{5.};  // but not constexpr
>      constexpr double kk = cc.get();
>
>  Why is this so? Object cc is subject to const initialization anyway. It is possible to make it work only by a seemingly irrelevant change in declaration (add constexpr). What was the reason for disallowing this "implicit generation of compile-time constants"?

This was a deliberate decision. Variables declared with const share
different responsibilities, while constexpr is much clearer. const means
(a) "cannot modify", it can also mean (b) constant initialization, *and*
it can mean (c) that the variable can be used within constant expressions.

const int n1 = 12;

satisfies all three requirements, while

int f();

const int n2 = f();

only the first. One other interesting corner case is

#include<mutex>

std::mutex m;

satisfies only the second (I'm ignoring here address-constant expression
within this discussion, which is a very special family of constant
expressions).

C++11 kept the very restricted use-case of const variables useable in
constant expressions which is the special bullet in 5.19 (emphasize mine):

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

but didn't invest the efforts to extend this for other literal types,
because the existing rules were surprising and not easy to understand.
The much simpler and more general bullet

"a non-volatile 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"

rules basically all the rest.

HTH&  Greetings from Bremen,

Daniel Kr  gler





--
[ comp.std.c++ is moderated.  To submit articles, try posting with your ]
[ newsreader.  If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]




Author: =?ISO-8859-2?Q?Andrzej_Krzemie=F1ski?= <akrzemi1@gmail.com>
Date: Sat, 17 Mar 2012 00:53:23 -0700 (PDT)
Raw View
W dniu czwartek, 15 marca 2012, 19:33:34 UTC+1 u   ytkownik Daniel
Kr   gler napisa   :
> Am 14.03.2012 21:33, schrieb Andrzej Krzemie   ski:
> >  Hi,
> >  This is my literal type:
> >
> >      struct C
> >      {
> >        double m;
> >        constexpr C( double m ) : m{m} {};
> >        constexpr double get() const { return m; };
> >      };
> >
> >  Therefore the following code works:
> >
> >      constexpr C c = C{5.};
> >      constexpr double k = c.get();
>
> Correct.
>
> >  But the following does not:
> >
> >      const C cc = C{5.};  // but not constexpr
> >      constexpr double kk = cc.get();
> >
> >  Why is this so? Object cc is subject to const initialization anyway. It is possible to make it work only by a seemingly irrelevant change in declaration (add constexpr). What was the reason for disallowing this "implicit generation of compile-time constants"?
>
> This was a deliberate decision. Variables declared with const share
> different responsibilities, while constexpr is much clearer. const means
> (a) "cannot modify", it can also mean (b) constant initialization, *and*
> it can mean (c) that the variable can be used within constant expressions.
>
> const int n1 = 12;
>
> satisfies all three requirements, while
>
> int f();
>
> const int n2 = f();
>
> only the first. One other interesting corner case is
>
> #include<mutex>
>
> std::mutex m;
>
> satisfies only the second (I'm ignoring here address-constant expression
> within this discussion, which is a very special family of constant
> expressions).
>
> C++11 kept the very restricted use-case of const variables useable in
> constant expressions which is the special bullet in 5.19 (emphasize mine):
>
> "a non-volatile glvalue of integral or enumeration type that refers to a
> non-volatile const object with a preceding initialization, **initialized
> with a constant expression**"
>
> but didn't invest the efforts to extend this for other literal types,
> because the existing rules were surprising and not easy to understand.
> The much simpler and more general bullet
>
> "a non-volatile 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"
>
> rules basically all the rest.
>
> HTH&  Greetings from Bremen,
>
> Daniel Kr   gler

I am now trying to understand the conditions in 5.19 P 2 and it
appears to me that the following initialization should work fine:

 struct C // C is same as above
 {
   double m;
   constexpr C( double m ) : m{m} {};
   constexpr double get() const { return m; };
 };

 C && rr = C{5.};  // no const!
 constexpr double kk = rr.get();

Reference rr is not even a reference to const. In the last line the
initializing expression involves an lvalue-to-rvalue conversion (or
not?) but I am protected by the third "unless" sub-bullet because
reference rr is a glvalue of literal type that refers to a
non-volatile temporary object whose lifetime has not ended,
initialized with a constant expression.

Am I missing something obvious here, or should the above example just
compile fine?

Regards,
&rzej


--
[ comp.std.c++ is moderated.  To submit articles, try posting with your ]
[ newsreader.  If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]




Author: Jason McKesson<jmckesson@gmail.com>
Date: Sat, 17 Mar 2012 07:50:15 -0700 (PDT)
Raw View
On Saturday, March 17, 2012 12:53:23 AM UTC-7, Andrzej Krzemie  ski wrote:
>  W dniu czwartek, 15 marca 2012, 19:33:34 UTC+1 u  ytkownik Daniel
>  Kr  gler napisa  :
>  >  Am 14.03.2012 21:33, schrieb Andrzej Krzemie  ski:
>  >  >   Hi,
>  >  >   This is my literal type:
>  >  >
>  >  >       struct C
>  >  >       {
>  >  >         double m;
>  >  >         constexpr C( double m ) : m{m} {};
>  >  >         constexpr double get() const { return m; };
>  >  >       };
>  >  >
>  >  >   Therefore the following code works:
>  >  >
>  >  >       constexpr C c = C{5.};
>  >  >       constexpr double k = c.get();
>  >
>  >  Correct.
>  >
>  >  >   But the following does not:
>  >  >
>  >  >       const C cc = C{5.};  // but not constexpr
>  >  >       constexpr double kk = cc.get();
>  >  >
>  >  >   Why is this so? Object cc is subject to const initialization anyway. It is possible to make it work only by a seemingly irrelevant change in declaration (add constexpr). What was the reason for disallowing this "implicit generation of compile-time constants"?
>  >
>  >  This was a deliberate decision. Variables declared with const share
>  >  different responsibilities, while constexpr is much clearer. const means
>  >  (a) "cannot modify", it can also mean (b) constant initialization, *and*
>  >  it can mean (c) that the variable can be used within constant expressions.
>  >
>  >  const int n1 = 12;
>  >
>  >  satisfies all three requirements, while
>  >
>  >  int f();
>  >
>  >  const int n2 = f();
>  >
>  >  only the first. One other interesting corner case is
>  >
>  >  #include<mutex>
>  >
>  >  std::mutex m;
>  >
>  >  satisfies only the second (I'm ignoring here address-constant expression
>  >  within this discussion, which is a very special family of constant
>  >  expressions).
>  >
>  >  C++11 kept the very restricted use-case of const variables useable in
>  >  constant expressions which is the special bullet in 5.19 (emphasize mine):
>  >
>  >  "a non-volatile glvalue of integral or enumeration type that refers to a
>  >  non-volatile const object with a preceding initialization, **initialized
>  >  with a constant expression**"
>  >
>  >  but didn't invest the efforts to extend this for other literal types,
>  >  because the existing rules were surprising and not easy to understand.
>  >  The much simpler and more general bullet
>  >
>  >  "a non-volatile 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"
>  >
>  >  rules basically all the rest.
>  >
>  >  HTH&   Greetings from Bremen,
>  >
>  >  Daniel Kr  gler
>
>  I am now trying to understand the conditions in 5.19 P 2 and it
>  appears to me that the following initialization should work fine:
>
>   struct C // C is same as above
>   {
>     double m;
>     constexpr C( double m ) : m{m} {};
>     constexpr double get() const { return m; };
>   };
>
>   C&&  rr = C{5.};  // no const!
>   constexpr double kk = rr.get();
>
>  Reference rr is not even a reference to const. In the last line the
>  initializing expression involves an lvalue-to-rvalue conversion (or
>  not?) but I am protected by the third "unless" sub-bullet because
>  reference rr is a glvalue of literal type that refers to a
>  non-volatile temporary object whose lifetime has not ended,
>  initialized with a constant expression.
>
>  Am I missing something obvious here, or should the above example just
>  compile fine?
>
>  Regards,
>  &rzej

C&&  rr = C{5.};  // no const!

`rr` is *not* a `constexpr`. That is becuase you did not declare it as such. It doesn't matter that `C` has a `constexpr` constructor; you must actually declare the object you construct `constexpr` for it to matter. Therefore:

constexpr double kk = rr.get();

You did declare `kk` as a `constexpr`. This means that it must be initialized by an expression that contains only `constexpr` values. Since `rr` is not a `constexpr` value, it cannot be used to initialize `kk`.


--
[ comp.std.c++ is moderated.  To submit articles, try posting with your ]
[ newsreader.  If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]




Author: =?ISO-8859-2?Q?Daniel_Kr=FCgler?=<daniel.kruegler@googlemail.com>
Date: Sun, 18 Mar 2012 11:31:13 -0700 (PDT)
Raw View
Am 17.03.2012 08:53, schrieb Andrzej Krzemie   ski:
>  I am now trying to understand the conditions in 5.19 P 2 and it
>  appears to me that the following initialization should work fine:
>
>    struct C // C is same as above
>    {
>      double m;
>      constexpr C( double m ) : m{m} {};
>      constexpr double get() const { return m; };
>    };
>
>    C&&   rr = C{5.};  // no const!
>
>    constexpr double kk = rr.get();
>
>  Reference rr is not even a reference to const. In the last line the
>  initializing expression involves an lvalue-to-rvalue conversion (or
>  not?) but I am protected by the third "unless" sub-bullet because
>  reference rr is a glvalue of literal type that refers to a
>  non-volatile temporary object whose lifetime has not ended,
>  initialized with a constant expression.
>
>  Am I missing something obvious here, or should the above example just
>  compile fine?

I don't think that you are missing something. This looks like a clear
defect in the wording, because it is also clear that it is not intended
to be well-formed. A similar problem applies to

const C&  lr = C{5.};

because any following code could *legally* apply const_cast<C&>(lr)
here, because the referenced value is *not* constant.

I'm forwarding your example (and the other one based on an
lvalue-reference) to the core language group.

Thanks&  Greetings from Bremen,

Daniel Kr   gler




--
[ comp.std.c++ is moderated.  To submit articles, try posting with your ]
[ newsreader.  If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]