Topic: Proposal: constexpr, non-const member functions


Author: "Richard Smith"<richard@metafoo.co.uk>
Date: Tue, 8 Nov 2011 11:32:36 -0800 (PST)
Raw View
Hi,

Pointers and references to non-const objects can generally be used within
constant expressions in C++11. However, there is a roadblock in the way of
anyone who wants to use them as the 'this' pointer, because constexpr member
functions are implicitly const. Consider a trivial class like this:

   template<typename T>
   class Wrapper {
     T v;
   public:
     constexpr Wrapper(const T&v) : v(v) {}
     T&get() { return v; } // note, can't be constexpr
     constexpr const T&get() { return v; }
     // ...
   };

This seems fine until you try to use a temporary of this type in a constant
expression:

   constexpr int n = Wrapper<int>(0).get(); // ill-formed!

This doesn't work, because the non-const (and thus non-constexpr) get()
overload  is selected.

Since C++11 is already out of the gate, it's too late to remove the 'constexpr
implies const' rule, so instead we could consider a syntax like this:

   constexpr T&get() mutable { return v; }

Does this seem like a useful extension?
--
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: Dave Abrahams <dave@boostpro.com>
Date: Tue, 8 Nov 2011 14:45:27 -0800 (PST)
Raw View
on Tue Nov 08 2011, "Richard Smith"<richard-AT-metafoo.co.uk> wrote:

> Since C++11 is already out of the gate, it's too late to remove the 'constexpr
> implies const' rule, so instead we could consider a syntax like this:
>
>    constexpr T&get() mutable { return v; }
>
> Does this seem like a useful extension?

I don't know whether the intent of your proposal makes sense (haven't
given it much thought) but in principle there's never any impediment to
making a previously-illegal usage legal, so this might not need new syntax.

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com


[ 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, 8 Nov 2011 14:47:15 -0800 (PST)
Raw View
Am 08.11.2011 20:32, schrieb Richard Smith:
>
> Hi,
>
> Pointers and references to non-const objects can generally be used within
> constant expressions in C++11. However, there is a roadblock in the way of
> anyone who wants to use them as the 'this' pointer, because constexpr member
> functions are implicitly const. Consider a trivial class like this:
>
>    template<typename T>
>    class Wrapper {
>      T v;
>    public:
>      constexpr Wrapper(const T&v) : v(v) {}
>      T&get() { return v; } // note, can't be constexpr
>      constexpr const T&get() { return v; }
>      // ...
>    };
>
> This seems fine until you try to use a temporary of this type in a constant
> expression:
>
>    constexpr int n = Wrapper<int>(0).get(); // ill-formed!
>
> This doesn't work, because the non-const (and thus non-constexpr) get()
> overload  is selected.
>
> Since C++11 is already out of the gate, it's too late to remove the 'constexpr
> implies const' rule, so instead we could consider a syntax like this:
>
>    constexpr T&get() mutable { return v; }
>
> Does this seem like a useful extension?

I completely agree with your analysis, I have the exactly same
impression of the status quo and of their inconsistencies. I
originally became aware of it by looking at the two basic kinds of
constant expressions, namely "value"-based ones (especially
rvalue-based), where a constant member function is the natural
solution for it, and "identity"-constant expressions (like
address-constant or reference-constant expressions), where both
constant and non-constant member functions are useful. It makes sense
to allow for a reference-constant expression even for non-const member
functions (or objects), because we can still have references (or
pointers) to non-const objects.

I tend to say that a good long-term suggestion would be to extend
mutable for member functions as you describe - the good news are that
lambda expressions already use this approach, so its not completely
new. My current short-term approach is to use free constexpr functions
as a workaround wherever possible. This is probably not the worst idea
as a general principle anyway, but enforcing it is actual against the
spirit of C++ and does not work for all functions (because some are
required to be member functions).

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: Marc <marc.glisse@gmail.com>
Date: Tue, 8 Nov 2011 14:47:24 -0800 (PST)
Raw View
"Richard Smith" wrote:

> Pointers and references to non-const objects can generally be used within
> constant expressions in C++11. However, there is a roadblock in the way of
> anyone who wants to use them as the 'this' pointer, because constexpr member
> functions are implicitly const. Consider a trivial class like this:
>
>    template<typename T>
>    class Wrapper {
>      T v;
>    public:
>      constexpr Wrapper(const T&v) : v(v) {}
>      T&get() { return v; } // note, can't be constexpr
>      constexpr const T&get() { return v; }
>      // ...
>    };
>
> This seems fine until you try to use a temporary of this type in a constant
> expression:
>
>    constexpr int n = Wrapper<int>(0).get(); // ill-formed!
>
> This doesn't work, because the non-const (and thus non-constexpr) get()
> overload  is selected.
>
> Since C++11 is already out of the gate, it's too late to remove the 'constexpr
> implies const' rule, so instead we could consider a syntax like this:
>
>    constexpr T&get() mutable { return v; }
>
> Does this seem like a useful extension?

Funny, there is currently a discussion about this on the gcc bugzilla.
std::bitset::operator[] has the same problem.

Can't you use *this references to solve this?

T&get()& { return v; }
constexpr const T&get()const& { return v; }


--
[ 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: Wed, 9 Nov 2011 21:54:04 -0800 (PST)
Raw View
On 2011-11-08 23:47, Marc wrote:
>
> Funny, there is currently a discussion about this on the gcc bugzilla.
> std::bitset::operator[] has the same problem.
>
> Can't you use *this references to solve this?
>
> T&get()&  { return v; }
> constexpr const T&get()const&  { return v; }

I think you can and this looks like an even better solution for this
particular example from Richard, which still is - based on my
imprecise nomenclature used in my reply to Richard - a constant
"value" expression.

I still think that we might want to allow for using mutable to mark a
non-static constexpr member function as a non-const function in all
cases where you want to conserve address-constness or
reference-constness. Consider this revised example based on Richard's
Wrapper type:

Wrapper<int> wi = ...; // In namespace-scope

constexpr int& ri = wi.get(); // Error
constexpr int* pi = &wi.get(); // Error

IMO there are no good reasons, why this example should be well-formed.
It currently is, because you cannot declare a non-const, but still
constexpr member function. It works fine, if you fall back to free
functions or static member functions, like so:

template<typename T>
class Wrapper {
 T v;
public:
 constexpr Wrapper(const T&v) : v(v) {}
 // ...

 friend constexpr T&get(Wrapper& w) // note, can be constexpr
 { return w.v; }
 friend constexpr const T&get(const Wrapper& w)
 { return w.v; }
};

Wrapper<int> wi = ...; // In namespace-scope

constexpr int& ri = get(wi); // OK
constexpr int* pi = &get(wi); // OK

This shows that constexpr functions do not necessarily need to be
const functions. They usually are, so the current default looks fine
and not like a defect to me. In the much rarer situations where
"identity"-based constant expressions are of interest, you should be
able to declare member functions the same way as free functions.

Summarizing, you may want to allow for the following:

template<typename T>
class Wrapper {
 T v;
public:
 constexpr Wrapper(const T&v) : v(v) {}
 constexpr T& get() mutable & { return v; }
 constexpr const T& get() & { return v; }
 // ...
};

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-1?Q?Daniel_Kr=FCgler?=<daniel.kruegler@googlemail.com>
Date: Wed, 9 Nov 2011 23:14:36 -0800 (PST)
Raw View
On 2011-11-10 06:54, Daniel Kr   gler wrote:
>  Consider this revised example based on Richard's
>  Wrapper type:
>
>  Wrapper<int>  wi = ...; // In namespace-scope
>
>  constexpr int&  ri = wi.get(); // Error
>  constexpr int* pi =&wi.get(); // Error
>
>  IMO there are no good reasons, why this example should be well-formed.

Arrgh, this should say:

IMO there are no good reasons, why this example should*n't* be well-formed.

I apologize for any confusion caused.

- Daniel


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