Restrictions on references in constant expressions

194 views
Skip to first unread message

dyp

unread,
Aug 11, 2015, 3:09:51 PM8/11/15
to std-dis...@isocpp.org
Please consider the following program:


#include <string>

constexpr bool is_same(std::string& lhs, std::string& rhs) {
return &lhs == &rhs;
}

int main() {
std::string s0, s1;
constexpr bool local_test = &s0 == &s1; // presumably well-formed
constexpr bool fun_test = is_same(s0, s1); // presumably ill-formed
}


Shafik Yaghmour has convinced me that [expr.const]p2.9 implies the
initialization of fun_test is not a constant expression: in the
comparison `&lhs == &rhs` within `is_same`:

- both lhs and rhs are id-expressions which are evaluated
- both refer to a reference
- which is not initialized with a constant expression (the automatic
string objects are not permitted results of constant expressions, and
the initializers s0 and s1 are lvalues)
- which do not refer to data members of objects whose lifetime begin
within the evaluation of `is_same(s0, s1)`

Yet, recent versions of both clang++ and g++ accept the above program
without diagnostic messages, and produce the expected result.


1) Is this interpretation of [expr.const] correct? Is the program really
ill-formed?
2) Is this program intended to be ill-formed?
3) Should the compilers be changed to produce diagnostic messages, or
should the rules be relaxed?


Thanks and kind regards,

dyp

Richard Smith

unread,
Aug 11, 2015, 4:11:06 PM8/11/15
to std-dis...@isocpp.org
This is a bug in the constant evaluation rules. It should be permissible to use an id-expression that refers to a variable of reference type if its lifetime began within the evaluation.
 
Thanks and kind regards,

dyp

--

---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussio...@isocpp.org.
To post to this group, send email to std-dis...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-discussion/.

dr.dsh...@gmail.com

unread,
Aug 12, 2015, 4:36:58 AM8/12/15
to ISO C++ Standard - Discussion, dyp...@gmx.net
Hello!
@dyp: Thanks for asking
@Richard: Thanks for answering.

So it looks like GCC 5.2.0-1 and CLANG 3.6.2-2 handle this wrong, even with -std=c++11 and -pedantic. Fascinating! Can someone maybe test IBM XL C/C++ and Microsoft Visucal C++, please?

Here is our original discussion on stackoverflow.com:

Byte
Peter


Edward Catmur

unread,
Aug 12, 2015, 6:41:34 AM8/12/15
to ISO C++ Standard - Discussion
To clarify, this is legal:

#include <string>
struct R { constexpr R(std::string& a) : r(a) {} std::string& r; };
constexpr bool is_same(R lhs, R rhs) { return &lhs.r == &rhs.r; }

int main() {
    std
::string s0, s1;

   
constexpr bool fun_test = is_same(s0, s1);
}

And the fix would be to add a bullet to [expr.const]/2.9 to make replacing R by std::string& also legal?
Message has been deleted

dr.dsh...@gmail.com

unread,
Aug 12, 2015, 10:33:14 AM8/12/15
to ISO C++ Standard - Discussion
Thank you for your clarification Edward. I misunterstood the answer of Richard at the first time, because I did't perceived the subsequent words after "This is a bug...".

My bad!

Richard Smith

unread,
Aug 12, 2015, 2:03:07 PM8/12/15
to std-dis...@isocpp.org
On Wed, Aug 12, 2015 at 3:41 AM, Edward Catmur <e...@catmur.co.uk> wrote:
To clarify, this is legal:

#include <string>
struct R { constexpr R(std::string& a) : r(a) {} std::string& r; };
constexpr bool is_same(R lhs, R rhs) { return &lhs.r == &rhs.r; }
int main() {
    std
::string s0, s1;

   
constexpr bool fun_test = is_same(s0, s1);
}

And the fix would be to add a bullet to [expr.const]/2.9 to make replacing R by std::string& also legal?

I asked to get a core issue opened for this; here's the wording change I suggested:

Change in 5.20 [expr.const] bullet 2.9 and remove the sub-bullets:

"an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and either
<del> -- </del> it is initialized with a constant expression or
<del> -- it is a non-static data member of an object whose<del><ins>its</ins> lifetime began within the evaluation of e;"

dyp

unread,
Aug 12, 2015, 2:13:46 PM8/12/15
to std-dis...@isocpp.org
Thank you very much for your answers and clarifications.

I might be nit-picking, but is the lifetime of a reference well enough
defined to be used in this wording? For objects, there are the extensive
[basic.life] rules, but for references, there's only the IMHO vague
[basic.stc]p3, "The lifetime of a reference is its storage duration."

There's a related defect: http://wg21.cmeerw.net/cwg/issue2012


Kind regards,

dyp


On 12.08.2015 20:03, Richard Smith wrote:
> On Wed, Aug 12, 2015 at 3:41 AM, Edward Catmur <e...@catmur.co.uk> wrote:
>
>> To clarify, this is legal:
>>
>> #include <string>
>> struct R { constexpr R(std::string& a) : r(a) {} std::string& r; };
>> constexpr bool is_same(R lhs, R rhs) { return &lhs.r == &rhs.r; }
>> int main() {
>> std::string s0, s1;
>> constexpr bool fun_test = is_same(s0, s1);
>> }
>>
>> And the fix would be to add a bullet to *[expr.const]*/2.9 to make

Richard Smith

unread,
Aug 12, 2015, 2:50:43 PM8/12/15
to std-dis...@isocpp.org
On Wed, Aug 12, 2015 at 11:13 AM, dyp <dyp...@gmx.net> wrote:
Thank you very much for your answers and clarifications.

I might be nit-picking, but is the lifetime of a reference well enough
defined to be used in this wording? For objects, there are the extensive
[basic.life] rules, but for references, there's only the IMHO vague
[basic.stc]p3, "The lifetime of a reference is its storage duration."

Yes, the lifetime of a reference is misspecified, but we should fix that directly rather than working around it elsewhere in the wording.
 
There's a related defect: http://wg21.cmeerw.net/cwg/issue2012


Kind regards,

dyp


On 12.08.2015 20:03, Richard Smith wrote:
> On Wed, Aug 12, 2015 at 3:41 AM, Edward Catmur <e...@catmur.co.uk> wrote:
>
>> To clarify, this is legal:
>>
>> #include <string>
>> struct R { constexpr R(std::string& a) : r(a) {} std::string& r; };
>> constexpr bool is_same(R lhs, R rhs) { return &lhs.r == &rhs.r; }
>> int main() {
>>     std::string s0, s1;
>>     constexpr bool fun_test = is_same(s0, s1);
>> }
>>
>> And the fix would be to add a bullet to *[expr.const]*/2.9 to make
>> replacing R by std::string& also legal?
>>
>
> I asked to get a core issue opened for this; here's the wording change I
> suggested:
>
> Change in 5.20 [expr.const] bullet 2.9 and remove the sub-bullets:
>
> "an id-expression that refers to a variable or data member of reference
> type unless the reference has a preceding initialization and either
> <del> -- </del> it is initialized with a constant expression or
> <del> -- it is a non-static data member of an object
> whose<del><ins>its</ins> lifetime began within the evaluation of e;"
>

Belloc

unread,
Dec 27, 2015, 8:22:46 AM12/27/15
to ISO C++ Standard - Discussion


On Tuesday, August 11, 2015 at 5:11:06 PM UTC-3, Richard Smith wrote:

This is a bug in the constant evaluation rules. It should be permissible to use an id-expression that refers to a variable of reference type if its lifetime began within the evaluation.
 

I'm having some difficulty understanding how  your suggestion for a core issue (see below) would solve this problem:

Change in 5.20 [expr.const] bullet 2.9 and remove the sub-bullets:

"an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and either
<del> -- </del> it is initialized with a constant expression or
<del> -- it is a non-static data member of an object whose<del><ins>its</ins> lifetime began within the evaluation of e;" 

The id-expressions lhs  and  rhs  would still refer to a variable with reference type whose initialization precedes the evaluation of the expression is_same(s0, s1). That is, the initialization of s0 and s1 in main() precede the inline expansion of the constexpr function  is_same() in the declaration  constexpr bool fun_test = is_same(s0, s1); . 

Richard Smith

unread,
Dec 28, 2015, 3:05:40 PM12/28/15
to std-dis...@isocpp.org
The "preceding initialization" part isn't the problem. The problem is that neither of the current bullets is satisfied: the references lhs and rhs are not initialized by constant expressions, and are not non-static data members, so they cannot be used within a constant expression. With the proposed change, they can be used, because their lifetimes began within the evaluation of the initializer of fun_test.

Belloc

unread,
Dec 28, 2015, 5:36:03 PM12/28/15
to ISO C++ Standard - Discussion


On Monday, December 28, 2015 at 6:05:40 PM UTC-2, Richard Smith wrote:

The "preceding initialization" part isn't the problem. The problem is that neither of the current bullets is satisfied: the references lhs and rhs are not initialized by constant expressions, and are not non-static data members, so they cannot be used within a constant expression. With the proposed change, they can be used, because their lifetimes began within the evaluation of the initializer of fun_test.

The lifetimes of the objects referenced by lhs and rhs didn't begin with the evaluation of the expression is_same(s0, s1) .

Columbo

unread,
Dec 28, 2015, 7:28:16 PM12/28/15
to ISO C++ Standard - Discussion
He refers to the references' lifetime. So does dyp's suggested wording (its last part).

Belloc

unread,
Dec 29, 2015, 6:16:53 AM12/29/15
to ISO C++ Standard - Discussion


On Monday, December 28, 2015 at 10:28:16 PM UTC-2, Columbo wrote:
He refers to the references' lifetime. So does dyp's suggested wording (its last part).

Well, "dyp's suggested wording" as you say, were:

I might be nit-picking, but is the lifetime of a reference well enough 
defined to be used in this wording? For objects, there are the extensive 
[basic.life] rules, but for references, there's only the IMHO vague 
[basic.stc]p3, "The lifetime of a reference is its storage duration."


To which, I would also add §12[class.temporary]/5:

"The second context is when a reference is bound to a temporary.117 The temporary to which the reference is
bound or the temporary that is the complete object of a subobject to which the reference is bound persists
for the lifetime of the reference except: ..."

And I agree with him, which IMHO means, issue 2012 needs to be approved and reflected in the Standard, so that the term "lifetime", as applied to a reference, is well defined. 

Columbo

unread,
Dec 29, 2015, 7:28:55 AM12/29/15
to ISO C++ Standard - Discussion
Sorry, it is Richard's suggested wording I meant, not dyp's - mea culpa.


On Tuesday, December 29, 2015 at 12:16:53 PM UTC+1, Belloc wrote:
And I agree with him, which IMHO means, issue 2012 needs to be approved and reflected in the Standard, so that the term "lifetime", as applied to a reference, is well defined. 
That may be correct, but is irrelevant to whether or not Richard's wording is sensible. Its last bullet point solely talks about the references' lifetimes, not the referees' lifetimes, making the above examples well-formed.

Belloc

unread,
Dec 29, 2015, 8:16:16 AM12/29/15
to ISO C++ Standard - Discussion
I should also have said in my prior post, that I finally understood Richard's intention, in reference to his suggested change in [expr.const].2 (2.9). 
Reply all
Reply to author
Forward
0 new messages