address-of operator should not produce a prvalue

219 views
Skip to first unread message

Spencer Simpson

unread,
Feb 26, 2015, 2:05:36 PM2/26/15
to std-pr...@isocpp.org
The address-of operator should produce an rvalue  (in the C++11 sense) rather than a prvalue. 
Alternatively, pointer (and scalar) prvalues should not bind to rvalue references.
 
 
A prvalue ("pure" rvalue) is an expression that identifies a temporary object (or a subobject thereof) or is a value not associated with any object.
 
but you can say that that the result of address-of is associated (in a loose sense) with an object -- the object it just took the address of.   It produces a pointer that could theoretically have the delete operator called on it somewhere.

Consider the following:
 
template <class S>
class sptr
{
S * m_obj;
bool m_mine;
void release() { if (m_mine) delete m_obj; m_obj=nullptr; }
 public:
sptr (S *const &i=nullptr): m_obj(i), m_mine(false) {}
sptr (S *&&i): m_obj(i), m_mine(true) {i=nullptr;}
sptr&operator= (S *const &i) { release(); m_obj=i; m_mine=false; return *this; }
sptr&operator= (S *&&i): { release(); m_obj=i; i=nullptr; m_mine=true; return *this; }
 // other stuff, you get the gist
~sptr ( release(); )
};
 
It's nice that I can now say
sptr<S> p_s (new S);

(because new produces an xvalue)

but I can also say

sptr<S> p_s;
S s;
p_s=&s; // uh-oh
Because &s in the second example is a prvalue, it binds to the move-assignment operator, making p_s.m_mine==true and thus sptr::release will attempt to delete an object it doesn't own.

Spencer Simpson

unread,
Feb 26, 2015, 2:19:03 PM2/26/15
to std-pr...@isocpp.org
Yes, the sptr destructor is a typo; it should read
~sptr() { release(); } 

David Rodríguez Ibeas

unread,
Feb 26, 2015, 3:12:14 PM2/26/15
to std-pr...@isocpp.org
On Thu, Feb 26, 2015 at 2:05 PM Spencer Simpson <ssim...@baltometro.org> wrote:
but you can say that that the result of address-of is associated (in a loose sense) with an object -- the object it just took the address of.

The result of the address-of operator is not associated with any **pointer** object.  In the context of that quote, "object" refers to an object of the same type than the prvalue expression.  Otherwise you have to consider that the iterators returned by 'std::list<T>::begin()' and 'end()' are not prvalues either, since they may also relate to some other object, and furthermore to an unrelated type.
 
   It produces a pointer that could theoretically have the delete operator called on it somewhere.

You can also call 'delete static_cast<int*>(0);' and clearly 0 is not associated with any object.

Consider the following:

You will just need to use a different design for your problem. I could suggest passing a flag indicating ownership, although I don't really believe in "sometimes"-owning smart pointers...

Spencer Simpson

unread,
Feb 26, 2015, 3:29:55 PM2/26/15
to std-pr...@isocpp.org, dib...@ieee.org


On Thursday, February 26, 2015 at 3:12:14 PM UTC-5, David Rodríguez Ibeas wrote:

The result of the address-of operator is not associated with any **pointer** object.  In the context of that quote, "object" refers to an object of the same type than the prvalue expression.

Thus, "in a loose sense".

| Otherwise you have to consider that the iterators returned by 'std::list<T>::begin()' and 'end()' are not prvalues either, since they may also relate to some other object, and furthermore to an unrelated type.

Well, if they're scalar, they needn't bind to rvalue references.  If not, they're beyond the scope of this proposal.
 
You can also call 'delete static_cast<int*>(0);' and clearly 0 is not associated with any object.

static_cast is not the address-of operator, which requires something to have its address taken.
 
You will just need to use a different design for your problem. I could suggest passing a flag indicating ownership, although I don't really believe in "sometimes"-owning smart pointers...

Although the example's design is off-topic, I like it regardless of whether this proposal is taken up or not. If circumstances force the design to be not entirely rigorous, I think it balances rigor and the problem to be solved adequately.   Example #2 is thus an instance of  "Doctor, it hurts when I do this".

Farid Mehrabi

unread,
Feb 27, 2015, 6:32:37 AM2/27/15
to std-proposals
I personally don't like use of new as the initializer of smart pointers when its possible to forward constructor parameters:

template<typename ...Args>
sptr::sptr(Args&&...args):m_mine{true},m_obj{new S{std::forward<Args>(args)...}{};

but again IMHO result of address operator should be const and so should the get member of smart pointers:

const pointer std::unique_ptr::get();

in contrast to:

pointer std::unique_ptr::release();

--

---
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/.



--
how am I supposed to end the twisted road of  your hair in the dark night??
unless the candle of your face does turn a lamp up on my way!!!

Daniel Krügler

unread,
Feb 27, 2015, 7:08:17 AM2/27/15
to std-pr...@isocpp.org
2015-02-27 12:32 GMT+01:00 Farid Mehrabi <farid....@gmail.com>:
> I personally don't like use of new as the initializer of smart pointers when
> its possible to forward constructor parameters:
>
> template<typename ...Args>
> sptr::sptr(Args&&...args):m_mine{true},m_obj{new
> S{std::forward<Args>(args)...}{};
>
> but again IMHO result of address operator should be const and so should the
> get member of smart pointers:
>
> const pointer std::unique_ptr::get();
>
> in contrast to:
>
> pointer std::unique_ptr::release();

It has never been part of the design goals of unique_ptr (or
shared_ptr) to propagate cv-qualifications of the wrapped pointer-like
thingee and accepting your proposal would break existing code. I
strongly suggest to introduce your own additional layering for these
purposes.

There currently exists a proposal to add such a library component
(albeit restricted to const qualifications) the standard library, see

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4372.html

- Daniel

Spencer Simpson

unread,
Feb 27, 2015, 8:48:35 AM2/27/15
to std-pr...@isocpp.org


On Friday, February 27, 2015 at 7:08:17 AM UTC-5, Daniel Krügler wrote:
2015-02-27 12:32 GMT+01:00 Farid Mehrabi <farid....@gmail.com>:

> but again IMHO result of address operator should be const and so should the
> get member of smart pointers:

It has never been part of the design goals of unique_ptr (or
shared_ptr) to propagate cv-qualifications of the wrapped pointer-like
thingee and accepting your proposal would break existing code.

To whose proposal are you responding?

I suppose I should not have used my custom shared pointer as an
example, because people are taking about that rather than the
address-of operator's unique behavior of exposing values
bound to objects that already exist.   .

The upshot of my proposal is: No good (and much harm) comes from
automatically binding &x to an rvalue reference, so it shouldn't do that.

Jean-Marc Bourguet

unread,
Feb 27, 2015, 10:50:49 AM2/27/15
to std-pr...@isocpp.org
Le jeudi 26 février 2015 20:05:36 UTC+1, Spencer Simpson a écrit :
The address-of operator should produce an rvalue  (in the C++11 sense) rather than a prvalue. 

Prvalues are rvalues.  xvalues are rvalues as well. I don't understand how what you propose would
solve your problem.  Whatever you do for & will be also valid for new.

Yours,

-- 
Jean-Marc

Spencer Simpson

unread,
Feb 27, 2015, 11:28:11 AM2/27/15
to std-pr...@isocpp.org

Prvalues are rvalues.  xvalues are rvalues as well. I don't understand how what you propose would
solve your problem.  Whatever you do for & will be also valid for new.

prvalues bind to rvalue-references. Ordinary rvalues do not. 

An xvalue (i.e. result of new) should always bind to an rvalue-rerefence.

Allowing &x to bind to an rvalue reference implies the pointer thus produced has move semantics, but in actual practice those semantics are never intended by that operator.

If & produced an ordinary rvalue rather than a prvalue, it wouldn't bind to an rvalue reference; but new (producing an xvalue) still would.

Tom Honermann

unread,
Feb 27, 2015, 11:51:46 AM2/27/15
to std-pr...@isocpp.org
On 02/27/2015 11:28 AM, Spencer Simpson wrote:
> If & produced an ordinary rvalue rather than a prvalue, it wouldn't bind
> to an rvalue reference; but new (producing an xvalue) still would.

The standard does not define an "ordinary rvalue". All expressions
produce either an lvalue, an xvalue, or a prvalue. The taxonomy and
text in C++11 3.10 [basic.lval] explain this.

Tom.

Howard Hinnant

unread,
Feb 27, 2015, 11:53:23 AM2/27/15
to std-pr...@isocpp.org
On Feb 27, 2015, at 11:28 AM, Spencer Simpson <ssim...@baltometro.org> wrote:

> If & produced an ordinary rvalue rather than a prvalue, it wouldn't bind to an rvalue reference; but new (producing an xvalue) still would.

I recommend reading section 3.10 of N4296:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf

It contains statements such as:

> Every expression belongs to exactly one of the fundamental classifications in this taxonomy: lvalue, xvalue, or prvalue.

According to Figure 1 in this section, an rvalue is an xvalue or a prvalue. “Ordinary rvalue” is not in our vocabulary.

A new expression is a prvalue. The expression resulting from calling std::addressof is also a prvalue. & applied to a lvalue scalar also produces a prvalue.

> An xvalue (i.e. result of new) should always bind to an rvalue-rerefence.

The result of new is a prvalue.

> prvalues bind to rvalue-references. Ordinary rvalues do not.

I can not dispute this as the standard does not define the term "ordinary rvalue". However for the standard definition of rvalue expression, they indeed will bind to cv-compatible rvalue references, whether the expression is an xvalue or prvalue.

This SO answer provides software you can run yourself to experimentally determine the value category of any expression:

http://stackoverflow.com/a/20721887/576911

Howard

Spencer Simpson

unread,
Feb 27, 2015, 12:02:39 PM2/27/15
to std-pr...@isocpp.org

> prvalues bind to rvalue-references. Ordinary rvalues do not.

I can not dispute this as the standard does not define the term "ordinary rvalue".  

Mea culpa: Too obsessed with differentiating copy semantics from move semantics

An experiment with

void Foo (int const &);

versus

void Foo (int &&);

showed that Foo(3)always called the second routine.

It does beg the question, do rvalue references even mean anything for scalars?

Howard Hinnant

unread,
Feb 27, 2015, 12:15:55 PM2/27/15
to std-pr...@isocpp.org
On Feb 27, 2015, at 12:02 PM, Spencer Simpson <ssim...@baltometro.org> wrote:

> It does beg the question, do rvalue references even mean anything for scalars?

Scalars will overload in the same way as all other object types, consistent with your experiment. What you do with those overloads is up to you. The sequence:

int i = 1;
int j = std::move(i);

will not modify the value of i, and will assign j the value 1.

Howard

Nicol Bolas

unread,
Feb 27, 2015, 12:27:17 PM2/27/15
to std-pr...@isocpp.org


On Friday, February 27, 2015 at 11:28:11 AM UTC-5, Spencer Simpson wrote:

Prvalues are rvalues.  xvalues are rvalues as well. I don't understand how what you propose would
solve your problem.  Whatever you do for & will be also valid for new.

prvalues bind to rvalue-references. Ordinary rvalues do not. 

An xvalue (i.e. result of new) should always bind to an rvalue-rerefence.

Allowing &x to bind to an rvalue reference implies the pointer thus produced has move semantics, but in actual practice those semantics are never intended by that operator.

And to me this is the crux of the issue.

What you want to do is to define semantics around the concept of moving vs. copying a pointer. If you take the address of something, you argue that the semantic intent is to copy the pointer, that you're not trying to transfer ownership of the pointer itself.

There are several problems here, syntactically. The first is that the address of operator can be overloaded (unfortunately). Which means that it's up to the overload to decide what to return. And this includes the value category. So there's no way to enforce these semantics globally.

The second is that std::addressof (the only solution if you really want to get the address of an object. Unfortunately) returns the object by value. So it's an xvalue. Which means, under your desired semantics, this would represent moving the object.

So implementing your semantics would require lots of changes. Some of which may not be backwards-compatible.

And personally... I can't say I agree with these semantics to begin with. Consider your code:

ptr_type<T> a = &b;
ptr_type<T> c = new T;

If I know nothing about ptr_type's implementation, my expectation is that both of these do the same thing. I would not expect 'a' and 'c' to be semantically any different. And therefore, if ptr_type is an owning smart pointer, I would very much expect the first line to fail code review, since it clearly says that you're sticking a pointer that can't be deleted into a smart pointer that will try to delete it.

I prefer being explicit about movement myself. If I were to have a smart pointer class that could be owning or non-owning, then I would want that expression to be explicit when it adopts a pointer (or doesn't adopt it). So at the very least, I would want to see something like this:

ptr_type<T> a = {&b, noadopt};
ptr_type<T> c = new T;

That way, it's immediately obvious what's going on. You don't need to know rules about what's a prvalue/xvalue/rvalue. You can read the code and understand what's going on. By default, the pointer is adopted, and if you don't want adoption, you use a different constructor. The deleters for shared_ptr and unique_ptr's are a great way to handle the unowning case.

Don't make the reader of your code have to know esoteric rules about value categories.

Tony V E

unread,
Feb 27, 2015, 12:35:42 PM2/27/15
to Standard Proposals

--



I do sometimes wonder if we should be able to better differentiate a prvalue from an xvalue - it might help with the "destructive move" ie an xvalue is never left hanging around - it really is going to be deleted, not reused, like a prvalue (IIUC).
I also sometimes wonder if we have too many type modifiers already :-)

Tony V E

unread,
Feb 27, 2015, 12:41:04 PM2/27/15
to Standard Proposals
On Fri, Feb 27, 2015 at 12:27 PM, Nicol Bolas <jmck...@gmail.com> wrote:


On Friday, February 27, 2015 at 11:28:11 AM UTC-5, Spencer Simpson wrote:

Prvalues are rvalues.  xvalues are rvalues as well. I don't understand how what you propose would
solve your problem.  Whatever you do for & will be also valid for new.

prvalues bind to rvalue-references. Ordinary rvalues do not. 

An xvalue (i.e. result of new) should always bind to an rvalue-rerefence.

Allowing &x to bind to an rvalue reference implies the pointer thus produced has move semantics, but in actual practice those semantics are never intended by that operator.

And to me this is the crux of the issue.

What you want to do is to define semantics around the concept of moving vs. copying a pointer. If you take the address of something, you argue that the semantic intent is to copy the pointer, that you're not trying to transfer ownership of the pointer itself.

There are several problems here, syntactically. The first is that the address of operator can be overloaded (unfortunately). Which means that it's up to the overload to decide what to return. And this includes the value category. So there's no way to enforce these semantics globally.

The second is that std::addressof (the only solution if you really want to get the address of an object. Unfortunately) returns the object by value. So it's an xvalue. Which means, under your desired semantics, this would represent moving the object.

So implementing your semantics would require lots of changes. Some of which may not be backwards-compatible.

And personally... I can't say I agree with these semantics to begin with. Consider your code:

ptr_type<T> a = &b;
ptr_type<T> c = new T;


If you could say that like:

ptr_type<T> a = &b;
ptr_type<T> c = std::move(new T);

it _might_ be more easily understood? And not require a specialized and wordy mylib::noadopt.

 
If I know nothing about ptr_type's implementation, my expectation is that both of these do the same thing. I would not expect 'a' and 'c' to be semantically any different. And therefore, if ptr_type is an owning smart pointer, I would very much expect the first line to fail code review, since it clearly says that you're sticking a pointer that can't be deleted into a smart pointer that will try to delete it.

I prefer being explicit about movement myself. If I were to have a smart pointer class that could be owning or non-owning, then I would want that expression to be explicit when it adopts a pointer (or doesn't adopt it). So at the very least, I would want to see something like this:

ptr_type<T> a = {&b, noadopt};
ptr_type<T> c = new T;

That way, it's immediately obvious what's going on. You don't need to know rules about what's a prvalue/xvalue/rvalue. You can read the code and understand what's going on. By default, the pointer is adopted, and if you don't want adoption, you use a different constructor. The deleters for shared_ptr and unique_ptr's are a great way to handle the unowning case.

Don't make the reader of your code have to know esoteric rules about value categories.


If & produced an ordinary rvalue rather than a prvalue, it wouldn't bind to an rvalue reference; but new (producing an xvalue) still would.

David Rodríguez Ibeas

unread,
Feb 27, 2015, 1:08:40 PM2/27/15
to std-pr...@isocpp.org
On Fri, Feb 27, 2015 at 12:41 PM, Tony V E <tvan...@gmail.com> wrote:
If you could say that like:

ptr_type<T> a = &b;
ptr_type<T> c = std::move(new T);

it _might_ be more easily understood? And not require a specialized and wordy mylib::noadopt.

No, 'new T' is a prvalue expression, calling 'std::move' on it does not change anything, new returns a *value* as the default address-of operator does also yield a *value* (the address of an object).  I am with Nicol here, code is for the maintainer, not the compiler. Explicitly stating whether ownership is being acquired is important enough to be spelled in code (It is also one, if not the main, reason why 'shared_ptr' and 'unique_ptr' constructors taking raw pointers are explicit).

    David

Spencer Simpson

unread,
Feb 27, 2015, 1:12:54 PM2/27/15
to std-pr...@isocpp.org

If I know nothing about ptr_type's implementation, my expectation is that both of these do the same thing.

If a class has both a copy constructor and a move constructor, that's part of the interface, and not an implementation detail.
 
I prefer being explicit about movement myself.

std::move should be explicit enough.
 

Don't make the reader of your code have to know esoteric rules about value categories.

Which is exactly what I wanted the proposal to do, make it absolutely clear what was copy and what was move, and not worry about weird cases, by limiting the types of things that bind to rvalue references.
I'm done; thanks for everybody's patience.

Thiago Macieira

unread,
Feb 27, 2015, 1:45:53 PM2/27/15
to std-pr...@isocpp.org
On Friday 27 February 2015 10:12:53 Spencer Simpson wrote:
> > Don't make the reader of your code have to know esoteric rules about
> > value
> > categories.
>
> Which is exactly what I wanted the proposal to do, make it absolutely clear
> what was copy and what was move, and not worry about weird cases, by
> limiting the types of things that bind to rvalue references.
> I'm done; thanks for everybody's patience.

It's pretty clear what is a copy and what is a move.

The problem is answering "what is a move for a scalar". Like Tony said:

int i = 1;
int j = std::move(i);

will "move" i to j.

The following is also a scalar move:

template <typename T> struct Container
{
T t;
// ...
Container &operator=(Container &&other)
{ using std::swap; swap(t, other.t); return *this; }
};

Container<int> i = { 1 };
Container<int> j;
j = std::move(i);

Just with a different behaviour.

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

Farid Mehrabi

unread,
Feb 28, 2015, 12:37:50 PM2/28/15
to std-proposals
2015-02-27 15:38 GMT+03:30 Daniel Krügler <daniel....@gmail.com>:

It has never been part of the design goals of unique_ptr (or
shared_ptr) to propagate cv-qualifications of the wrapped pointer-like
thingee and accepting your proposal would break existing code. I
strongly suggest to introduce your own additional layering for these
purposes.


Kind of you to reply but it has not been my point too.
 
There currently exists a proposal to add such a library component
(albeit restricted to const qualifications) the standard library, see

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4372.html
 
Good link but my post wasn`t about cv propagation. The const specifier was meant to affect the pointer itself,not the pointed to object. AFAIK const rvalues can`t bind to none const rvalue references. Prior to the rvalue reference proposal, traditionally we used to consider rvalues as const(sort of). In my opinion the pointer returned by any operator or function that refers to a solid pointer should be const so the pointer(not the pointed to object) can not be moved from. IMHO the OP`s example shows a defect in current standard due to the fact that most of us are not used to apply const specifier on the return type of value returning functions. I propose that the default signature of '&x' should be:

T pointed_cv * const T::operator&() pointed_cv ;

also:

T pointed_cv * const unique_ptr<pointed_cv>::operator->();

and:

T pointed_cv * const std::unique_ptr<T pointed_cv>::get() const;

in contrast to:

T pointed_cv * std::unique_ptr<T pointed_cv>::release();

same discussion goes for 'shared_ptr' or any [smart]  pointer type.

where "pointed_cv" refers to cv-qualification of pointed to object while 'const' is affecting the [smart] pointer.
Should I add the full 'template' declarations to disambiguate all the above?

I just re-downloaded http://www.stroustrup.com/terminology.pdf to remember the insane meanings of "xpg"s. So where should the 'const rvalue' be placed on the "W"-shaped diagram? Isn`t this an "IM"? what shall we call it? Still there are IMHO large gaps between the language constructs and theoretical terms.

By the way I am a const-o-phile. It ('const') is good to encourage optimizations, good to prevent unintended modifications, ...
I have a hysteria of using const on return types by default, unless proved to be more advantageous otherwise. So it just took me an instant to shoot the upfront beast with my old favorite weapon. My proposal is now targeting the solution towards OP`s suggested dilemma, without changing any existing ABI; Nobody - I guess - has applied OP`s approach in production code but if my humble response is accepted, Someone will. I have always felt sore about using raw pointers for initializing smart pointers because of the points observed in the OP`s approach. IMHO facilitating this approach might improve code safety, by preventing a shared_ptr/unique_ptr/smart_ptr from being accidentally initialized with '&x'. 

regards,
FM
 

Daniel Krügler

unread,
Feb 28, 2015, 1:25:16 PM2/28/15
to std-pr...@isocpp.org
2015-02-28 18:37 GMT+01:00 Farid Mehrabi <farid....@gmail.com>:
>> There currently exists a proposal to add such a library component
>> (albeit restricted to const qualifications) the standard library, see
>>
>> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4372.html
>>
>
> Good link but my post wasn`t about cv propagation.

OK, I misunderstood your suggestion, I (falsly) assumed that you had a
typo in your code when you suggested to add const to the returned
type.

> The const specifier was
> meant to affect the pointer itself,not the pointed to object.

Can you please elaborate why you the returned pointer value itself
should be const? Especially since C++11 with move semantics I haven't
seen good use-cases to return const prvalues. For non-class types this
const-qualification will be removed during expression analysis anyway.
For class-types it only prevents the move-semantics. Once I get the
value as a non-const value, I still can modify it.

> AFAIK const
> rvalues can`t bind to none const rvalue references.

You are mislead. The language requires that the following code is well-formed:

int const foo() { return 42; }

int&& ri = foo();

int main() {}

It will work for class-types. But for what reasons do you want this property?

> Prior to the rvalue
> reference proposal, traditionally we used to consider rvalues as const(sort
> of).

This code style is no longer recommended since C++11. I never have
considered the C++03 recommendation as useful in my own code bases.

>In my opinion the pointer returned by any operator or function that
> refers to a solid pointer should be const so the pointer(not the pointed to
> object) can not be moved from.

But why? Please provide some convincing examples where this is really helpful.

>IMHO the OP`s example shows a defect in
> current standard due to the fact that most of us are not used to apply const
> specifier on the return type of value returning functions.

Why should they? The const-qualifier-sequence of a function returning
a value is (in general) not related to a possible const-qualification
of the return value. To the contrary: const-qualifying the return
value of such a function prevents useful things and I see now real
advantage when it is applied.

> I propose that
> the default signature of '&x' should be:
>
> T pointed_cv * const T::operator&() pointed_cv ;
>
> also:
>
> T pointed_cv * const unique_ptr<T pointed_cv>::operator->();
>
> and:
>
> T pointed_cv * const std::unique_ptr<T pointed_cv>::get() const;
>
> in contrast to:
>
> T pointed_cv * std::unique_ptr<T pointed_cv>::release();
>
> same discussion goes for 'shared_ptr' or any [smart] pointer type.

I'm strongly opposed to those changes, especially since I'm not seeing
any advantage for these additional signatures. const-qualifying
functions makes sense to propagate const, such as I quoted in my
previous response, but in your example there is no real usefulness for
this.
>
> where "pointed_cv" refers to cv-qualification of pointed to object while
> 'const' is affecting the [smart] pointer.
> Should I add the full 'template' declarations to disambiguate all the above?
>
> I just re-downloaded http://www.stroustrup.com/terminology.pdf to remember
> the insane meanings of "xpg"s.

What precisely are you meaning by xpg? Assuming you meant xvalues,
prvalues, and glvalues: Why should these meanings be insane?

> So where should the 'const rvalue' be placed
> on the "W"-shaped diagram? Isn`t this an "IM"? what shall we call it?

Why should a const prvalue something different than a prvalue?

> Still
> there are IMHO large gaps between the language constructs and theoretical
> terms.

Sorry, I fail to see these gaps in regard to value categories.

> By the way I am a const-o-phile. It ('const') is good to encourage
> optimizations, good to prevent unintended modifications, ...

Well, const has some good reasons, but it seems to me that your
suggestion does not belong to one of those.

> I have a hysteria of using const on return types by default, unless proved
> to be more advantageous otherwise. So it just took me an instant to shoot
> the upfront beast with my old favorite weapon. My proposal is now targeting
> the solution towards OP`s suggested dilemma, without changing any existing
> ABI; Nobody - I guess - has applied OP`s approach in production code but if
> my humble response is accepted, Someone will. I have always felt sore about
> using raw pointers for initializing smart pointers because of the points
> observed in the OP`s approach. IMHO facilitating this approach might improve
> code safety, by preventing a shared_ptr/unique_ptr/smart_ptr from being
> accidentally initialized with '&x'.

I fail to see what kind of "code-safety" your suggestions would bring
to the C++ community.

- Daniel

Nicol Bolas

unread,
Feb 28, 2015, 1:29:37 PM2/28/15
to std-pr...@isocpp.org

Except that it isn't "absolutely clear".

shared_ptr<T> t = new T;

That isn't a move. Or at least, not how that concept is generally taught. It seems like a transfer of ownership, but it isn't technically doing so. Nothing owned the pointer to begin with, so you're creating ownership (via construction), rather than transferring it.

By contrast:

shared_ptr<T> t = std::move(some_unique_ptr);

This is a logical transfer of ownership. The unique_ptr owned it, and the ownership is moved into the shared_ptr.

What understanding your code requires is an understanding of the language differences between a pointer generated by `&X` and a pointer generated by `new T`. Those are the esoteric differences I was talking about.

Nevin Liber

unread,
Feb 28, 2015, 7:06:51 PM2/28/15
to std-pr...@isocpp.org
On 28 February 2015 at 18:37, Farid Mehrabi <farid....@gmail.com> wrote:
By the way I am a const-o-phile. It ('const') is good to encourage optimizations,

Does it?  I've not found it to help optimizations all that much.  Could you post a few examples in something like GCC Explorer <http://gcc.godbolt.org> where the mere addition of a "const" both didn't change semantics and helped optimization?  Benchmarks as well for that code would be even better (as it isn't known to be an optimization w/o measurements).
 
IMHO facilitating this approach might improve code safety, by preventing a shared_ptr/unique_ptr/smart_ptr from being accidentally initialized with '&x'. 

I've never seen this done accidentally.  I have seen brand new beginners to the language do this deliberately because they didn't understand smart pointers, but education about ownership and lifetime proved far more valuable.  Certainly better than adding a new obscure corner to the language that would be all but unteachable to anyone but an expert.
--
 Nevin ":-)" Liber  <mailto:ne...@eviloverlord.com(847) 691-1404

Arthur O'Dwyer

unread,
Mar 2, 2015, 3:08:15 AM3/2/15
to std-pr...@isocpp.org
On Saturday, February 28, 2015 at 4:06:51 PM UTC-8, Nevin ":-)" Liber wrote:
On 28 February 2015 at 18:37, Farid Mehrabi <farid....@gmail.com> wrote:
 
IMHO facilitating this approach might improve code safety, by preventing a shared_ptr/unique_ptr/smart_ptr from being accidentally initialized with '&x'. 

I've never seen this done accidentally.  I have seen brand new beginners to the language do this deliberately because they didn't understand smart pointers, but education about ownership and lifetime proved far more valuable.

I think it's time to come out and say openly that Farid and Spencer are suffering from a misconception of what "ownership" means in this context, and from a confusion between "pointer object" and "pointed-to object". Going all the way back to Spencer's original post:

A prvalue ("pure" rvalue) is an expression that identifies a temporary object (or a subobject thereof) or is a value not associated with any object.
but you can say that that the result of address-of is associated (in a loose sense) with an object -- the object it just took the address of.

*You* could say that, but C++ doesn't. The value (or temporary object) identified by &x (that is, a pointer with value 0x0000000028947304) is not associated with any other object; it is certainly not associated with x (an int object with value 5).
Similarly, the value (or temporary object) identified by -x (that is, an int with value -5) is not associated with x (an int object with value 5), even though "you could say that the result of unary-minus is associated (in a loose sense) with an object — the object it just negated."

Your idea that operator& should return a const-qualified object is a bad idea. It should (and does) return a pointer, which is conceptually the same kind of thing as a shared_ptr or your sptr. In fact, there's a proposal out already (N4282) to introduce observer_ptr<T>, "the world's dumbest smart pointer", which has the same ownership guarantees as good old T*. Consider the following function signature:

    observer_ptr<T> take_address(T& t) { return make_observer(&t); }

   
int main() {
       
int i = 42;
        observer_ptr
<int> obs;
        obs
= take_address(i);  // does this move-assign or copy-assign?
   
}

You (Spencer and Farid) should read up on move semantics until you have convinced yourself that it would be a Very Bad Idea to replace the preceding code with

    const observer_ptr<T> take_address(T& t) { return make_observer(&t); }

Now that you're convinced, let's substitute operator& for take_address and T* for observer_ptr<T>. You have now convinced yourself that

    T* const operator&(T& t) { ... }

is the wrong signature for operator&. Excellent! Our work here is done.

HTH,
Arthur

Matthew Woehlke

unread,
Mar 2, 2015, 1:14:14 PM3/2/15
to std-pr...@isocpp.org
On 2015-02-28 12:37, Farid Mehrabi wrote:
> I propose that the default signature of '&x' should be:
>
> T pointed_cv * const T::operator&() pointed_cv ;

No. In fact, don't even write code like that (ever); the 'const' there
is useless and at least GCC ignores it entirely, and can warn you that
it is doing so (see -Wignored-qualifiers). Actually, "useless" is not
strong enough; it's *actively harmful* as it gives an impression of
const-ness that does not exist.

(I've seen one library, though I forget which offhand, that had a
horribly broken API due to this misunderstanding.)

> By the way I am a const-o-phile.

Me too :-). But never in the above case; in fact I routinely build with
-Werror=ignored-qualifiers.

p.s. Don't declare by-value parameters 'const' (in a declaration) for
the same reason :-). (In a definition it's okay and even good, as it
actually means something in that case... though I admit that I rarely do
that...)

--
Matthew

Farid Mehrabi

unread,
Mar 3, 2015, 3:00:53 PM3/3/15
to std-proposals
2015-02-28 21:55 GMT+03:30 Daniel Krügler <daniel....@gmail.com>:
2015-02-28 18:37 GMT+01:00 Farid Mehrabi <farid....@gmail.com>:
>> There currently exists a proposal to add such a library component
>> (albeit restricted to const qualifications) the standard library, see
>>
>> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4372.html
>>
>
> Good link but my post wasn`t about cv propagation.

OK, I misunderstood your suggestion, I (falsly) assumed that you had a
typo in your code when you suggested to add const to the returned
type.

> The const specifier was
> meant to affect the pointer itself,not the pointed to object.

Can you please elaborate why you the returned pointer value itself
should be const? Especially since C++11 with move semantics I haven't
seen good use-cases to return const prvalues. For non-class types this
const-qualification will be removed during expression analysis anyway.
For class-types it only prevents the move-semantics. Once I get the
value as a non-const value, I still can modify it.

> AFAIK const
> rvalues can`t bind to none const rvalue references.

You are mislead. The language requires that the following code is well-formed:

int const foo() { return 42; }

int&& ri = foo();

int main() {}

It will work for class-types. But for what reasons do you want this property?


It is but why should it be well-formed?
I t was based on wrong assumptions, but let`s pretend that your example is ill-formed: now the difference between const and mutable pointer rvalues can be used to distinguish owned and orphaned pointers.
 
>
> where "pointed_cv" refers to cv-qualification of pointed to object while
> 'const' is affecting the [smart] pointer.
> Should I add the full 'template' declarations to disambiguate all the above?
>
> I just re-downloaded http://www.stroustrup.com/terminology.pdf to remember
> the insane meanings of "xpg"s.

What precisely are you meaning by xpg? Assuming you meant xvalues,
prvalues, and glvalues: Why should these meanings be insane?

> So where should the 'const rvalue' be placed
> on the "W"-shaped diagram? Isn`t this an "IM"? what shall we call it?

Why should a const prvalue something different than a prvalue?


the 'const' qualifier is supposed to prevent modifications - including moving from - the rvalue that is already lacking identity. By striping it of its mobility, it falls completely out of the diagram. 

> Still
> there are IMHO large gaps between the language constructs and theoretical
> terms.

Sorry, I fail to see these gaps in regard to value categories.


Although currently built-in type 'const rvalues' are treated as prvalues, class types seem to be not: They are still 'IM'.
 
> By the way I am a const-o-phile. It ('const') is good to encourage
> optimizations, good to prevent unintended modifications, ...

Well, const has some good reasons, but it seems to me that your
suggestion does not belong to one of those.

I fail to see what kind of "code-safety" your suggestions would bring
to the C++ community.

- Daniel

distinction between mobile and immobile pointers can be one reason.

regards,
FM. 
 
--
how am I supposed to end the twisted road of  your hair in the dark night??
unless the candle of your face does sheds some light up on my way!!!

Matthew Woehlke

unread,
Mar 3, 2015, 3:32:08 PM3/3/15
to std-pr...@isocpp.org
On 2015-03-03 15:00, Farid Mehrabi wrote:
> 2015-02-28 21:55 GMT+03:30 Daniel Krügler wrote:
>> You are mislead. The language requires that the following code is
>> well-formed:
>>
>> int const foo() { return 42; }
>>
>> int&& ri = foo();
>>
>> int main() {}
>>
>> It will work for class-types. But for what reasons do you want this
>> property?
>
> It is but why should it be well-formed?

...because the 'const' is meaningless in that context. The above is
*exactly* equivalent to:

int foo() { return 42; }
int&& ri = foo();
int main() {}

At least in GCC, there is *no way at all* to tell the difference between
those two inputs¹. In fact, GCC simply drops the 'const' from the above
example.

(¹ Well, there is one way; -Wignored-qualifiers will emit a diagnostic
for the first. What I mean is that they will behave the same, generate
the exact same AST and output code, and there is nothing within the
source to tell them apart.)

And, like I said elsewhere; *don't ever write the first version*. Don't
put a 'const' there; it's terrible practice.

If you want that code to be ill-formed, you must first change the
standard to require that a const return value means something. (Which...
might be adequate for your original purpose, but is a MUCH more
significant change than you originally suggested. Also likely one with
serious compatibility issues.)

>> Why should a const prvalue something different than a prvalue?
>
> the 'const' qualifier is supposed to prevent modifications - including
> moving from - the rvalue that is already lacking identity. By striping it
> of its mobility, it falls completely out of the diagram.

Well, it doesn't, and the const qualifier here doesn't mean anything and
is *ignored entirely* by compilers.

--
Matthew

Farid Mehrabi

unread,
Mar 3, 2015, 4:25:50 PM3/3/15
to std-proposals
don`t yell plz, I am not deaf. 
 
significant change than you originally suggested. Also likely one with
serious compatibility issues.)


Significance in terms of  difficulty in compiler modification I guess. but compatibility? marking the built-in return type as const should have been very rare I believe ( only in case some level of paranoia akin to mine might have encouraged anybody).

>> Why should a const prvalue something different than a prvalue?
>
> the 'const' qualifier is supposed to prevent modifications - including
> moving from - the rvalue that is already lacking identity. By striping it
> of its mobility, it falls completely out of the diagram.

Well, it doesn't, and the const qualifier here doesn't mean anything and
is *ignored entirely* by compilers.

I figured it earlier (and that is about built-in types only) . and as I think you implied,  the const qualifier could make a difference that would find use cases unless the standard had prevented (which in our case has indeed) it from being so.


--
how am I supposed to end the twisted road of  your hair in the dark night??
unless the candle of your face does shed some light up on my way!!!

Thiago Macieira

unread,
Mar 3, 2015, 5:00:18 PM3/3/15
to std-pr...@isocpp.org
On Wednesday 04 March 2015 00:55:27 Farid Mehrabi wrote:
> Significance in terms of difficulty in compiler modification I guess. but
> compatibility? marking the built-in return type as const should have been
> very rare I believe ( only in case some level of paranoia akin to mine
> might have encouraged anybody).

Sorry, but you might be surprised.

I remember this consulting company that added const everywhere to a codebase
because "it couldn't hurt"...

That's quite different from adding auto to all automatic variables in C++98
because no one has taught auto since the early 1970s and the B language.

Matthew Woehlke

unread,
Mar 3, 2015, 5:28:10 PM3/3/15
to std-pr...@isocpp.org
On 2015-03-03 16:25, Farid Mehrabi wrote:
> 2015-03-04 0:01 GMT+03:30 Matthew Woehlke:
>> [...] you must first change the standard to require that a const
>> return value means something. (Which... might be adequate for your
>> original purpose, but is a MUCH more significant change than you
>> originally suggested. Also likely one with serious compatibility
>> issues.)
>
> Significance in terms of difficulty in compiler modification I guess. but
> compatibility? marking the built-in return type as const should have been
> very rare I believe ( only in case some level of paranoia akin to mine
> might have encouraged anybody).

Yes, compatibility. As previously mentioned, naïve programmers have been
incorrectly marking return values as 'const'. Currently this means
nothing. If it suddenly starts to mean something, than any case where
the const or lack thereof is relevant (and those must exist, or there is
no point to making such a change) will suddenly have different behavior
(e.g. call different overloads, or just fail to compile).

BTW, if such a change is made, then what would be the type of 'foo' in
this example? 'int' or 'const int'? What if 'auto' is replaced with
'auto&&'?

int const bar();
auto foo = bar();

--
Matthew

Magnus Fromreide

unread,
Mar 3, 2015, 6:22:02 PM3/3/15
to std-pr...@isocpp.org
On Tue, Mar 03, 2015 at 05:27:58PM -0500, Matthew Woehlke wrote:
> On 2015-03-03 16:25, Farid Mehrabi wrote:
> > 2015-03-04 0:01 GMT+03:30 Matthew Woehlke:
> >> [...] you must first change the standard to require that a const
> >> return value means something. (Which... might be adequate for your
> >> original purpose, but is a MUCH more significant change than you
> >> originally suggested. Also likely one with serious compatibility
> >> issues.)
> >
> > Significance in terms of difficulty in compiler modification I guess. but
> > compatibility? marking the built-in return type as const should have been
> > very rare I believe ( only in case some level of paranoia akin to mine
> > might have encouraged anybody).
>
> Yes, compatibility. As previously mentioned, naïve programmers have been
> incorrectly marking return values as 'const'. Currently this means
> nothing.

It actually do mean something - annoyance and trouble.

Consider a library with many current clients and declarations like the
following one:

struct foo_base {
virtual const int bar();
};

In this case removing the 'const' in the base class will trigger compilation
failures in the clients and that makes a gradual migration away from the
unnecessary const version harder.

/MF

Nicol Bolas

unread,
Mar 3, 2015, 8:41:55 PM3/3/15
to std-pr...@isocpp.org, mw_t...@users.sourceforge.net


On Tuesday, March 3, 2015 at 3:32:08 PM UTC-5, Matthew Woehlke wrote:
On 2015-03-03 15:00, Farid Mehrabi wrote:
> 2015-02-28 21:55 GMT+03:30 Daniel Krügler wrote:
>> You are mislead. The language requires that the following code is
>> well-formed:
>>
>> int const foo() { return 42; }
>>
>> int&& ri = foo();
>>
>> int main() {}
>>
>> It will work for class-types. But for what reasons do you want this
>> property?
>
> It is but why should it be well-formed?

...because the 'const' is meaningless in that context.

Not entirely.

If you return a value by 'const', then the caller cannot bind it to a non-const lvalue or rvalue. Now granted, I can't see a good reason why you would want to prevent the user from doing either of these two things.

But there is a difference.

Matthew Woehlke

unread,
Mar 4, 2015, 11:00:36 AM3/4/15
to std-pr...@isocpp.org
On 2015-03-03 20:41, Nicol Bolas wrote:
> On Tuesday, March 3, 2015 at 3:32:08 PM UTC-5, Matthew Woehlke wrote:
>> ...because the 'const' is meaningless in ['int const foo(...)'].
>
> Not entirely.
>
> If you return a value by 'const', then the caller cannot bind it to a
> non-const lvalue <https://ideone.com/C7KWGn>

Actually, that's not legal with or without the const.

> or rvalue <https://ideone.com/bhxlAN>.

...and that's also a class type. On a *class type* it means something.
On a POD type, which is what I used in my example, and which is what
this entire thread has been talking about (pointers, specifically), it
does not; see https://ideone.com/eCERHi (which is const yet has no errors).

That said, returning a const class by value still seems dubious...

--
Matthew

Matthew Woehlke

unread,
Mar 4, 2015, 11:07:49 AM3/4/15
to std-pr...@isocpp.org
On 2015-03-03 18:21, Magnus Fromreide wrote:
> On Tue, Mar 03, 2015 at 05:27:58PM -0500, Matthew Woehlke wrote:
>> Currently [marking POD return values 'const'] means nothing.
>
> It actually do mean something - annoyance and trouble.
>
> Consider a library with many current clients and declarations like the
> following one:
>
> struct foo_base {
> virtual const int bar();
> };
>
> In this case removing the 'const' in the base class will trigger compilation
> failures in the clients and that makes a gradual migration away from the
> unnecessary const version harder.

Hmm... Okay, the const doesn't mean anything *else*. That said, I'd
almost consider it a defect that override resolution considers a
meaningless token.

--
Matthew

Farid Mehrabi

unread,
Mar 4, 2015, 1:12:28 PM3/4/15
to std-proposals
Just one question: why should a library programmer pay the extra typing time to qualify a return type as 'const' while he dose not really mean that the result is supposed to be const?

regards,
FM.

--

---
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/.



--
how am I supposed to end the twisted road of  your hair in the dark night??
unless the candle of your face does turn a lamp up on my way!!!

Magnus Fromreide

unread,
Mar 4, 2015, 1:36:18 PM3/4/15
to std-pr...@isocpp.org
On Wed, Mar 04, 2015 at 09:42:06PM +0330, Farid Mehrabi wrote:
> Just one question: why should a library programmer pay the extra typing
> time to qualify a return type as 'const' while he dose not really mean that
> the result is supposed to be const?

The thread did have a reference to "naïve programmers (who) have been
incorrectly marking return values as 'const'" that fell away in Mathhew's
answer.

The reasoning could be that adding const to object return values prevents
mutating operations on the returned temporaries and having them on the
non-object return values will then be consistent and can't harm anything.

Then the interface gets set in stone and ensures lasting pain.

/MF

Matthew Woehlke

unread,
Mar 4, 2015, 1:52:27 PM3/4/15
to std-pr...@isocpp.org
On 2015-03-04 13:36, Magnus Fromreide wrote:
> On Wed, Mar 04, 2015 at 09:42:06PM +0330, Farid Mehrabi wrote:
>> Just one question: why should a library programmer pay the extra typing
>> time to qualify a return type as 'const' while he dose not really mean that
>> the result is supposed to be const?
>
> The thread did have a reference to "naïve programmers (who) have been
> incorrectly marking return values as 'const'" that fell away in Mathhew's
> answer.
>
> The reasoning could be that adding const to object return values prevents
> mutating operations on the returned temporaries and having them on the
> non-object return values will then be consistent and can't harm anything.
>
> Then the interface gets set in stone and ensures lasting pain.

I may need to go find what library it was that was doing this, because
this was NOT the problem. It was in fact the case that someone misused
const, where it was *very clear* that the intention was to return a
pointer to a const object, but in fact had no effect. I stumbled across
it specifically because I was trying to figure out why a bunch of
objects that clearly should have been const were not.

More concretely, this was happening:

typedef MyRealObject* MyObject;
const MyObject GetImmutableInstance();

That is, the return was literally a 'const ObjectType', where ObjectType
was a typedef of a pointer type. For various reasons, it was quite
obvious that the intent had been to return a pointer to const object.

(I suppose if anything this is more an argument for why that sort of
thing really ought to just be a hard error, but anyway the point that a
const POD return is identical¹ to a non-const POD return is critical to
the OP's request. In the sense that the request is not possible without
changing that.)

(¹ Except, obnoxiously, that compilers will bitch and moan if the const
doesn't match when overriding virtual methods, despite that being the
*only* way in which the const has any meaning. IMO that's almost as bad
as saying that the parameter names must match in order to override...)

--
Matthew

Farid Mehrabi

unread,
Mar 4, 2015, 2:09:37 PM3/4/15
to std-proposals
2015-03-04 22:06 GMT+03:30 Magnus Fromreide <ma...@lysator.liu.se>:
On Wed, Mar 04, 2015 at 09:42:06PM +0330, Farid Mehrabi wrote:
> Just one question: why should a library programmer pay the extra typing
> time to qualify a return type as 'const' while he dose not really mean that
> the result is supposed to be const?

The thread did have a reference to "naïve programmers (who) have been
incorrectly marking return values as 'const'" that fell away in Mathhew's
answer.

The reasoning could be that adding const to object return values prevents
mutating operations on the returned temporaries and having them on the
non-object return values will then be consistent and can't harm anything.

if the programmer has assumed similarity between object & non-object const return , then the code is already ill-formed but will be fixed by proposed uniformity. the only case that needs further thought is type deduction due to 'auto' or 'decltype' which are relatively new. let us remember that old code on old compilers assumed that rvalues can only bind to const references, though some compilers did not comply with the standard in that regard. So I think it is not late to change the standard iff the need for the proposed change is strong.
at least the confusion about use of cv on non-object return will be removed as well as inconsistency between object and non-object types.
 
regards,
FM.

> regards,
> FM.
>
> 2015-03-04 19:37 GMT+03:30 Matthew Woehlke <mw_t...@users.sourceforge.net>:
>
> > On 2015-03-03 18:21, Magnus Fromreide wrote:
> > > On Tue, Mar 03, 2015 at 05:27:58PM -0500, Matthew Woehlke wrote:
> > >> Currently [marking POD return values 'const'] means nothing.
> > >
> > > It actually do mean something - annoyance and trouble.
> > >
> > > Consider a library with many current clients and declarations like the
> > > following one:
> > >
> > > struct foo_base {
> > >       virtual const int bar();
> > > };
> > >
> > > In this case removing the 'const' in the base class will trigger
> > compilation
> > > failures in the clients and that makes a gradual migration away from the
> > > unnecessary const version harder.
> >
> > Hmm... Okay, the const doesn't mean anything *else*. That said, I'd
> > almost consider it a defect that override resolution considers a
> > meaningless token.
> >

--

---
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/.

Thiago Macieira

unread,
Mar 4, 2015, 5:57:15 PM3/4/15
to std-pr...@isocpp.org
On Wednesday 04 March 2015 11:00:11 Matthew Woehlke wrote:
> ...and that's also a class type. On a *class type* it means something.
> On a POD type, which is what I used in my example, and which is what
> this entire thread has been talking about (pointers, specifically), it
> does not; see https://ideone.com/eCERHi (which is const yet has no errors).

That is not proof. I don't know which compiler it ran, but it's unlikely it
ran one of those that are known to encode constness of POD parameters and
return types (that come to mind: Sun Studio and Visual Studio).

Matthew Woehlke

unread,
Mar 4, 2015, 6:27:36 PM3/4/15
to std-pr...@isocpp.org
On 2015-03-04 17:57, Thiago Macieira wrote:
> On Wednesday 04 March 2015 11:00:11 Matthew Woehlke wrote:
>> ...and that's also a class type. On a *class type* it means something.
>> On a POD type, which is what I used in my example, and which is what
>> this entire thread has been talking about (pointers, specifically), it
>> does not; see https://ideone.com/eCERHi (which is const yet has no errors).
>
> That is not proof. I don't know which compiler it ran, but it's unlikely it
> ran one of those that are known to encode constness of POD parameters and
> return types (that come to mind: Sun Studio and Visual Studio).

I did mention GCC specifically in my original reply. I'm not sure what
ideone is using, but I also tested locally with GCC.

Still, either GCC is non-conforming (seems unlikely), or the standard
allows the compiler to disregard the 'const'. If the latter case, to me
that says that having a const POD return type is dubious if not outright
dangerous, regardless of what some particular compiler does. (If the
standard allows it to be ignored, what's to stop a compiler from
changing its behavior in some future release?)

--
Matthew

Thiago Macieira

unread,
Mar 4, 2015, 9:09:30 PM3/4/15
to std-pr...@isocpp.org
On Wednesday 04 March 2015 18:27:21 Matthew Woehlke wrote:
> Still, either GCC is non-conforming (seems unlikely), or the standard
> allows the compiler to disregard the 'const'. If the latter case, to me
> that says that having a const POD return type is dubious if not outright
> dangerous, regardless of what some particular compiler does. (If the
> standard allows it to be ignored, what's to stop a compiler from
> changing its behavior in some future release?)

I thought we were talking about virtual overrides differing in the constness. I
didn't actually read your source code, I only searched the site to see if it
had an option to select a different compiler.

TBH, I don't know about this particular scenario. PODs and non-PODs differ in
behaviour as to what you can do to their rvalues:

template <typename T> T returnIt();
template <typename T> void modifyIt1()
{
returnIt<T>() = T{};
}
template <typename T> void modifyIt2(T &&t) { t = T{}; }

struct S {};
int main()
{
// does not compile
modifyIt1<int>();

// compiles:
modifyIt1<S>();

// compiles:
modifyIt2(returnIt<const int>());

// does not compile
modifyIt2(returnIt<const S>());
}

In one case, you can modify an rvalue POD but not the rvalue non-POD; in the
other, you can modify the rvalue non-POD but can't the rvalue POD.

If GCC's implementation is non-conforming, then so is Clang's and ICC's. But
it would make sense if they were and the third case above weren't allowed: no
binding of a const prvalue to a non-const rvalue, regardless of PODness.

Matthew Woehlke

unread,
Mar 5, 2015, 11:23:02 AM3/5/15
to std-pr...@isocpp.org
On 2015-03-04 21:09, Thiago Macieira wrote:
> On Wednesday 04 March 2015 18:27:21 Matthew Woehlke wrote:
>> Still, either GCC is non-conforming (seems unlikely), or the standard
>> allows the compiler to disregard the 'const'. If the latter case, to me
>> that says that having a const POD return type is dubious if not outright
>> dangerous, regardless of what some particular compiler does. (If the
>> standard allows it to be ignored, what's to stop a compiler from
>> changing its behavior in some future release?)
>
> I thought we were talking about virtual overrides differing in the constness.

That was elsewhere in the thread :-).

> TBH, I don't know about this particular scenario. PODs and non-PODs differ in
> behaviour as to what you can do to their rvalues:
>
> template <typename T> T returnIt();
> template <typename T> void modifyIt1() { returnIt<T>() = T{}; }
>
> int main()
> {
> // compiles:
> modifyIt2(returnIt<const int>());

That... is very surprising. It does NOT compile if the return type is
not templated, i.e.:

const int test();
void mutate() { test() = 0; } // nope; error

> }
>
> In one case, you can modify an rvalue POD but not the rvalue non-POD; in the
> other, you can modify the rvalue non-POD but can't the rvalue POD.
>
> If GCC's implementation is non-conforming, then so is Clang's and ICC's. But
> it would make sense if they were and the third case above weren't allowed: no
> binding of a const prvalue to a non-const rvalue, regardless of PODness.

Do you mean that you think the third case compiling is an error? (If
yes, I would be rather inclined to agree.) I can't think of a good
reason why this should be allowed... I *could* argue that the template
case should allow the assignment regardless, for the sake of generic
programming, but it already doesn't because the non-const doesn't allow
the assignment either. It actually feels quite bizarre that the const
would *allow* assignment while the non-const does not.

(I have more sympathy for eliding the -Wignored-qualifiers diagnostic
here, since it would be somewhat obnoxious to avoid it in the template
case.)

I guess I can understand allowing modification of a non-POD rvalue,
since that could potentially have side effects (e.g. in operator=, or if
the destructor behaves differently depending on the value).

--
Matthew

Richard Smith

unread,
Mar 5, 2015, 2:43:24 PM3/5/15
to std-pr...@isocpp.org
On Thu, Mar 5, 2015 at 8:22 AM, Matthew Woehlke <mw_t...@users.sourceforge.net> wrote:
On 2015-03-04 21:09, Thiago Macieira wrote:
> On Wednesday 04 March 2015 18:27:21 Matthew Woehlke wrote:
>> Still, either GCC is non-conforming (seems unlikely), or the standard
>> allows the compiler to disregard the 'const'. If the latter case, to me
>> that says that having a const POD return type is dubious if not outright
>> dangerous, regardless of what some particular compiler does. (If the
>> standard allows it to be ignored, what's to stop a compiler from
>> changing its behavior in some future release?)
>
> I thought we were talking about virtual overrides differing in the constness.

That was elsewhere in the thread :-).

> TBH, I don't know about this particular scenario. PODs and non-PODs differ in
> behaviour as to what you can do to their rvalues:
>
> template <typename T> T returnIt();
> template <typename T> void modifyIt1() { returnIt<T>() = T{}; }
>
> int main()
> {
>       // compiles:
>       modifyIt2(returnIt<const int>());

That... is very surprising. It does NOT compile if the return type is
not templated, i.e.:

  const int test();
  void mutate() { test() = 0; } // nope; error

Thiago's example calls modifyIt2, not modifyIt1.
 

> }
>
> In one case, you can modify an rvalue POD but not the rvalue non-POD; in the
> other, you can modify the rvalue non-POD but can't the rvalue POD.
>
> If GCC's implementation is non-conforming, then so is Clang's and ICC's. But
> it would make sense if they were and the third case above weren't allowed: no
> binding of a const prvalue to a non-const rvalue, regardless of PODness.

Do you mean that you think the third case compiling is an error? (If
yes, I would be rather inclined to agree.) I can't think of a good
reason why this should be allowed... I *could* argue that the template
case should allow the assignment regardless, for the sake of generic
programming, but it already doesn't because the non-const doesn't allow
the assignment either. It actually feels quite bizarre that the const
would *allow* assignment while the non-const does not.

(I have more sympathy for eliding the -Wignored-qualifiers diagnostic
here, since it would be somewhat obnoxious to avoid it in the template
case.)

I guess I can understand allowing modification of a non-POD rvalue,
since that could potentially have side effects (e.g. in operator=, or if
the destructor behaves differently depending on the value).

--
Matthew

Matthew Woehlke

unread,
Mar 5, 2015, 3:23:22 PM3/5/15
to std-pr...@isocpp.org
On 2015-03-05 14:43, Richard Smith wrote:
> On Thu, Mar 5, 2015 at 8:22 AM, Matthew Woehlke wrote:
>> On 2015-03-04 21:09, Thiago Macieira wrote:
>>> On Wednesday 04 March 2015 18:27:21 Matthew Woehlke wrote:
>>>> Still, either GCC is non-conforming (seems unlikely), or the standard
>>>> allows the compiler to disregard the 'const'. If the latter case, to me
>>>> that says that having a const POD return type is dubious if not outright
>>>> dangerous, regardless of what some particular compiler does. (If the
>>>> standard allows it to be ignored, what's to stop a compiler from
>>>> changing its behavior in some future release?)
>>>
>>> TBH, I don't know about this particular scenario. PODs and non-PODs
>>> differ in behaviour as to what you can do to their rvalues:
>>>
>>> template <typename T> T returnIt();
>>> template <typename T> void modifyIt1() { returnIt<T>() = T{}; }
>>>
>>> int main()
>>> {
>>> // compiles:
>>> modifyIt2(returnIt<const int>());
>>
>> const int test();
>> void mutate() { test() = 0; } // nope; error
>
> Thiago's example calls modifyIt2, not modifyIt1.

D'oh. Well... but that was just my original point; both the const and
non-const convert to POD&& and can be modified after such conversion.
(Or rather, there is no such thing as a 'const POD rvalue'... except for
override resolution.)

--
Matthew

Reply all
Reply to author
Forward
0 new messages