Topic: Defect report: constexpr and mutable members of literal types


Author: "Richard Smith"<richard@metafoo.co.uk>
Date: Wed, 19 Oct 2011 15:23:47 -0700 (PDT)
Raw View
Hi,

The C++11 IS allows literal types to have mutable members. In particular,
programs like the following are legal:

struct MM {
   mutable int n;
};

template<int n>  struct S { int k = n; };

int f(int k) {
   constexpr MM m = { 0 }; // ok, MM is a literal type
   m.n = k;                // ok, m.n is mutable
   return S<m.n>().k;      // ok, an lvalue-to-rvalue conversion of
                           // a subobject of a constexpr object is
                           // a constant expression [expr.const]/2
}

There seem to be two natural ways to fix this. Either (a) an lvalue-to-rvalue
conversion on a mutable member of a constexpr object should be considered to
not be a constant expression, or (b) literal types should not be allowed to
have mutable members.

(b) seems like the better solution, since it allows constexpr objects to be
ROM-able, which the constexpr papers note as an explicit goal. The clang
compiler currently implements (b).

Thanks,
Richard


--
[ 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-1?Q?Daniel_Kr=FCgler?=<daniel.kruegler@googlemail.com>
Date: Tue, 25 Oct 2011 09:59:55 -0700 (PDT)
Raw View
Am 20.10.2011 00:23, schrieb Richard Smith:
>  Hi,
>
>  The C++11 IS allows literal types to have mutable members. In particular,
>  programs like the following are legal:
>
>  struct MM {
>      mutable int n;
>  };
>
>  template<int n>    struct S { int k = n; };
>
>  int f(int k) {
>      constexpr MM m = { 0 }; // ok, MM is a literal type
>      m.n = k;                // ok, m.n is mutable
>      return S<m.n>().k;      // ok, an lvalue-to-rvalue conversion of
>                              // a subobject of a constexpr object is
>                              // a constant expression [expr.const]/2
>  }
>
>  There seem to be two natural ways to fix this. Either (a) an lvalue-to-rvalue
>  conversion on a mutable member of a constexpr object should be considered to
>  not be a constant expression, or (b) literal types should not be allowed to
>  have mutable members.
>
>  (b) seems like the better solution, since it allows constexpr objects to be
>  ROM-able, which the constexpr papers note as an explicit goal. The clang
>  compiler currently implements (b).

Forwarded to CWG.

But let me ask, why you prefer (b)? IMO approach (a) seems more
consistent to existing rules. IMO it should suffice that the expression
m.n is not a constant expression in this example. I don't think that we
have or need the guarantee that *all* literal types are "ROMable"
(depending on the precise definition). E.g. the following should already
be well-formed:

struct RefConstant {
   int&  ri;
   constexpr RefConstant(int&  ri) : ri(ri) {}
};

int x = 1;

constexpr RefConstant rc(x);

int main() {
   rc.ri = 2; // OK
}

where rc.ri can be used in reference-constant expressions. But this
still allows to modify the memory that rc.ri refers to. Arguably,
mutable data have "value semantics (at least typically), but what I'm
trying to point out is, that the compiler already has to look at what
you try to do with the members of a literal type.

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: Richard Smith<richard@metafoo.co.uk>
Date: Wed, 26 Oct 2011 11:19:33 -0700 (PDT)
Raw View
On Oct 25, 9:59 am, Daniel Kr   gler<daniel.krueg...@googlemail.com>
wrote:
>  Am 20.10.2011 00:23, schrieb Richard Smith:
>  >    The C++11 IS allows literal types to have mutable members. In particular,
>  >    programs like the following are legal:
>
>  >    struct MM {
>  >        mutable int n;
>  >    };
>
>  >    template<int n>      struct S { int k = n; };
>
>  >    int f(int k) {
>  >        constexpr MM m = { 0 }; // ok, MM is a literal type
>  >        m.n = k;                // ok, m.n is mutable
>  >        return S<m.n>().k;      // ok, an lvalue-to-rvalue conversion of
>  >                                // a subobject of a constexpr object is
>  >                                // a constant expression [expr.const]/2
>  >    }
>
>  >    There seem to be two natural ways to fix this. Either (a) an lvalue-to-rvalue
>  >    conversion on a mutable member of a constexpr object should be considered to
>  >    not be a constant expression, or (b) literal types should not be allowed to
>  >    have mutable members.
>
>  >    (b) seems like the better solution, since it allows constexpr objects to be
>  >    ROM-able, which the constexpr papers note as an explicit goal. The clang
>  >    compiler currently implements (b).
>
>  Forwarded to CWG.
>
>  But let me ask, why you prefer (b)? IMO approach (a) seems more
>  consistent to existing rules. IMO it should suffice that the expression
>  m.n is not a constant expression in this example. I don't think that we
>  have or need the guarantee that *all* literal types are "ROMable"
>  (depending on the precise definition). E.g. the following should already
>  be well-formed:
>
>  struct RefConstant {
>      int&    ri;
>      constexpr RefConstant(int&    ri) : ri(ri) {}
>
>  };
>
>  int x = 1;
>
>  constexpr RefConstant rc(x);
>
>  int main() {
>      rc.ri = 2; // OK
>
>  }
>
>  where rc.ri can be used in reference-constant expressions. But this
>  still allows to modify the memory that rc.ri refers to. Arguably,
>  mutable data have "value semantics (at least typically), but what I'm
>  trying to point out is, that the compiler already has to look at what
>  you try to do with the members of a literal type.

The initializer for rc here is still a (dynamic-)link-time constant.
In an environment without load-time relocation, rc's initializer's
value can be baked into the binary and even burned into a ROM chip,
and in an environment with load-time relocation, rc can be loaded into
memory pages marked read-only. The same would not be true if
RefConstant contained a mutable member.

Providing a keyword for which implementations can give a 'will be
placed into read-only memory' guarantee seemed to be one of the
motivations of constexpr, so a compiler diagnostic for cases where
that guarantee is unavailable seems appropriate; I believe this can
only happen currently if a constexpr object has a mutable member. If
the committee's intended guarantee was the weaker guarantee that
constexpr objects won't have dynamic initialization, then I agree that
(a) is probably the more appropriate choice.

Richard


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