Discussion:
I believe this is a deffect in [basic.def.odr]/3 in N4140
(too old to reply)
l***@gmail.com
2015-05-12 12:59:41 UTC
Permalink
I'm struggling with this paragraph for almost 2 weeks already. But I
believe I have found something inaccurate with the current text in
[basic.def.odr]/3 which I repeat below:

A variable x whose name appears as a potentially-evaluated expression ex is
odr-used by ex unless applying the lvalue-to-rvalue conversion (4.1) to x
yields a constant expression (5.19) that does not invoke any nontrivial
functions and, if x is an object, ex is an element of the set of potential
results of an expression e, where either the lvalue-to-rvalue conversion
(4.1) is applied to e, or e is a discarded-value expression (Clause 5).

Consider this simple example:

const int i = 1;
int f(int i) { return i; }
int j = f(i);



According to [basic,def.odr]/3 the variable i is *odr-used* which is
clearly *an error*. To verify this, note that i is odr-used unless
applying the lvalue-to-rvalue conversion to x yields a constant expression
that does not invoke any nontrivial function. That is true. But the second
condition is false, as i is an object, but i is *not* an element of the set
of potential results of the expression f(i), because this set is empty,
according to [basic.def.odr]/2. Therefore, we conclude from
[basic.def.odr]/3 that i is odr-used.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-discussion/.
l***@gmail.com
2015-05-12 13:06:53 UTC
Permalink
Post by l***@gmail.com
According to [basic,def.odr]/3 the variable i is *odr-used* which is
clearly *an error*. To verify this, note that i is odr-used unless
applying the lvalue-to-rvalue conversion to x yields a constant
expression that does not invoke any nontrivial function. That is true.
But the second condition is false, as i is an object, but i is *not* an
element of the set of potential results of the expression f(i), because
this set is empty, according to [basic.def.odr]/2. Therefore, we conclude
from [basic.def.odr]/3 that i is odr-used.
This is the correct sentence:

According to [basic,def.odr]/3 the variable i is *odr-used* which is
clearly *an error*. To verify this, note that i is odr-used unless
applying the lvalue-to-rvalue conversion to *i* yields a constant
expression that does not invoke any nontrivial function. That is true. But
the second condition is false, as i is an object, but i is *not* an element
of the set of potential results of the expression f(i), because this set is
empty, according to [basic.def.odr]/2. Therefore, we conclude from
[basic.def.odr]/3 that i is odr-used.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-discussion/.
dyp
2015-05-12 13:15:31 UTC
Permalink
As far as I understand the wording, ex is the id-subexpression i of
the expression f(i), because ex names the variable i. In other words,
the variable i appears as the potentially-evaluated id-expression i.
This id-expression i is an element of the set of potential results of
itself as per [basic.def.odr]p2.1.


Now, I _think_ that the lvalue-to-rvalue conversion is applied to the
subexpression i of the expression f(i) in order to copy-initialize the
function parameter of f. But I cannot find anything in the Standard
that mandates the lvalue-to-rvalue conversion for such kinds of
initialization.


Kind regards,

dyp
Post by l***@gmail.com
I'm struggling with this paragraph for almost 2 weeks already. But
I believe I have found something inaccurate with the current text
A variable x whose name appears as a potentially-evaluated
expression ex is odr-used by ex unless applying the
lvalue-to-rvalue conversion (4.1) to x yields a constant expression
(5.19) that does not invoke any nontrivial functions and, if x is
an object, ex is an element of the set of potential results of an
expression e, where either the lvalue-to-rvalue conversion (4.1) is
applied to e, or e is a discarded-value expression (Clause 5).
const int i = 1; int f(int i) { return i; } int j = f(i);
According to [basic,def.odr]/3 the variable i is *odr-used* which
is clearly *an error*. To verify this, note that i is odr-used
unless applying the lvalue-to-rvalue conversion to x yields a
constant expression that does not invoke any nontrivial function.
That is true. But the second condition is false, as i is an object,
but i is *not* an element of the set of potential results of the
expression f(i), because this set is empty, according to
[basic.def.odr]/2. Therefore, we conclude from [basic.def.odr]/3
that i is odr-used.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-discussion/.
Leon Trotski
2015-05-12 13:29:14 UTC
Permalink
I believe the e in the second condition of [basic.def.odr]/3 cannot be a
sub-expression of another expression, for this condition was put in place
to avoid the error shown in DR 712
<http://www.open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#712>.
Therefore the e in [basic.def.odr]/3 refers to f(i) in my example, and not
to its sub-expression i.
Post by dyp
As far as I understand the wording, ex is the id-subexpression i of
the expression f(i), because ex names the variable i. In other words,
the variable i appears as the potentially-evaluated id-expression i.
This id-expression i is an element of the set of potential results of
itself as per [basic.def.odr]p2.1.
Now, I _think_ that the lvalue-to-rvalue conversion is applied to the
subexpression i of the expression f(i) in order to copy-initialize the
function parameter of f. But I cannot find anything in the Standard
that mandates the lvalue-to-rvalue conversion for such kinds of
initialization.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-discussion/.
David Krauss
2015-05-12 13:42:12 UTC
Permalink
I believe the e in the second condition of [basic.def.odr]/3 cannot be a sub-expression of another expression, for this condition was put in place to avoid the error shown in DR 712 <http://www.open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#712>. Therefore the e in [basic.def.odr]/3 refers to f(i) in my example, and not to its sub-expression i.
No, e can be any subexpression. In this case it’s i.

If the function argument was cond? i : i, then i would still not be ODR-used, because the lvalue of i is a potential result of that subexpression too, and the lvalue-to-rvalue conversion is applied by the function call.

The wording change in DR 712 doesn’t mention full-expressions. It clarifies how to reliably process conditional-expressions, which are allowed to “simultaneously” convert multiple variables in this static analysis. More generally, it describes how to trace from any given lvalue-to-rvalue-converted expression to the eligible id-expressions within it.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-discussion/.
Leon Trotski
2015-05-12 14:00:15 UTC
Permalink
By your interpretation of [basic.def.odr]/3 applied to the following
example, one would say that the variable i is not odr-used, which is
clearly wrong!

const int i = 1;
const int& f(const int& i) { return i; }
int j = f(i);
Post by Leon Trotski
I believe the e in the second condition of [basic.def.odr]/3 cannot be a
sub-expression of another expression, for this condition was put in place
to avoid the error shown in DR 712
<http://www.open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#712>.
Therefore the e in [basic.def.odr]/3 refers to f(i) in my example, and
not to its sub-expression i.
No, e can be any subexpression. In this case it’s i.
If the function argument was cond? i : i, then i would still not be
ODR-used, because the lvalue of i is a potential result of that
subexpression too, and the lvalue-to-rvalue conversion is applied by the
function call.
The wording change in DR 712 doesn’t mention full-expressions. It
clarifies how to reliably process conditional-expressions, which are
allowed to “simultaneously” convert multiple variables in this static
analysis. More generally, it describes how to trace from any given
lvalue-to-rvalue-converted expression to the eligible id-expressions within
it.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-discussion/.
Richard Smith
2015-05-12 20:11:43 UTC
Permalink
Post by Leon Trotski
By your interpretation of [basic.def.odr]/3 applied to the following
example, one would say that the variable i is not odr-used, which is
clearly wrong!
const int i = 1;
const int& f(const int& i) { return i; }
int j = f(i);
Here, the lvalue-to-rvalue conversion is not applied to the expression 'i',
so the variable 'i' is odr-used.
Post by Leon Trotski
Post by Leon Trotski
I believe the e in the second condition of [basic.def.odr]/3 cannot be a
sub-expression of another expression, for this condition was put in place
to avoid the error shown in DR 712
<http://www.open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#712>.
Therefore the e in [basic.def.odr]/3 refers to f(i) in my example, and
not to its sub-expression i.
No, e can be any subexpression. In this case it’s i.
If the function argument was cond? i : i, then i would still not be
ODR-used, because the lvalue of i is a potential result of that
subexpression too, and the lvalue-to-rvalue conversion is applied by the
function call.
The wording change in DR 712 doesn’t mention full-expressions. It
clarifies how to reliably process conditional-expressions, which are
allowed to “simultaneously” convert multiple variables in this static
analysis. More generally, it describes how to trace from any given
lvalue-to-rvalue-converted expression to the eligible id-expressions within
it.
--
---
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
Visit this group at
http://groups.google.com/a/isocpp.org/group/std-discussion/.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-discussion/.
Andrew Tomazos
2015-05-12 20:19:17 UTC
Permalink
It seems pretty clear by now that this paragraph of the standard is
basically unreadable. I wonder if there is a way to reword it so that it
is clearer. Failing that, perhaps a note and/or an example or two.
-Andrew.
Post by Richard Smith
Post by Leon Trotski
By your interpretation of [basic.def.odr]/3 applied to the following
example, one would say that the variable i is not odr-used, which is
clearly wrong!
const int i = 1;
const int& f(const int& i) { return i; }
int j = f(i);
Here, the lvalue-to-rvalue conversion is not applied to the expression
'i', so the variable 'i' is odr-used.
Post by Leon Trotski
Post by Leon Trotski
I believe the e in the second condition of [basic.def.odr]/3 cannot be
a sub-expression of another expression, for this condition was put in place
to avoid the error shown in DR 712
<http://www.open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#712>.
Therefore the e in [basic.def.odr]/3 refers to f(i) in my example, and
not to its sub-expression i.
No, e can be any subexpression. In this case it’s i.
If the function argument was cond? i : i, then i would still not be
ODR-used, because the lvalue of i is a potential result of that
subexpression too, and the lvalue-to-rvalue conversion is applied by the
function call.
The wording change in DR 712 doesn’t mention full-expressions. It
clarifies how to reliably process conditional-expressions, which are
allowed to “simultaneously” convert multiple variables in this static
analysis. More generally, it describes how to trace from any given
lvalue-to-rvalue-converted expression to the eligible id-expressions within
it.
--
---
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
Visit this group at
http://groups.google.com/a/isocpp.org/group/std-discussion/.
--
---
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
Visit this group at
http://groups.google.com/a/isocpp.org/group/std-discussion/.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-discussion/.
Faisal Vali
2015-05-12 20:31:44 UTC
Permalink
Post by Andrew Tomazos
It seems pretty clear by now that this paragraph of the standard is
basically unreadable. I wonder if there is a way to reword it so that it is
clearer. Failing that, perhaps a note and/or an example or two. -Andrew.
It's also currently a little broken - i think there's a DR that Hubert
recently reported about the invocation of non-trivial functions during
the l-t-rval conversion - so hopefuly during that rewording, either
the wording becomes clearer or additional illustrative examples will
get added (so please continue to be especially nice to core, so they
don't decide to make it even more convoluted ;)
Post by Andrew Tomazos
Post by Richard Smith
Post by Leon Trotski
By your interpretation of [basic.def.odr]/3 applied to the following
example, one would say that the variable i is not odr-used, which is clearly
wrong!
const int i = 1;
const int& f(const int& i) { return i; }
int j = f(i);
Here, the lvalue-to-rvalue conversion is not applied to the expression
'i', so the variable 'i' is odr-used.
Post by Leon Trotski
Post by Leon Trotski
I believe the e in the second condition of [basic.def.odr]/3 cannot be a
sub-expression of another expression, for this condition was put in place to
avoid the error shown in DR 712. Therefore the e in [basic.def.odr]/3 refers
to f(i) in my example, and not to its sub-expression i.
No, e can be any subexpression. In this case it’s i.
If the function argument was cond? i : i, then i would still not be
ODR-used, because the lvalue of i is a potential result of that
subexpression too, and the lvalue-to-rvalue conversion is applied by the
function call.
The wording change in DR 712 doesn’t mention full-expressions. It
clarifies how to reliably process conditional-expressions, which are allowed
to “simultaneously” convert multiple variables in this static analysis. More
generally, it describes how to trace from any given
lvalue-to-rvalue-converted expression to the eligible id-expressions within
it.
--
---
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
Visit this group at
http://groups.google.com/a/isocpp.org/group/std-discussion/.
--
---
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
Visit this group at
http://groups.google.com/a/isocpp.org/group/std-discussion/.
--
---
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
Visit this group at
http://groups.google.com/a/isocpp.org/group/std-discussion/.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-discussion/.
David Krauss
2015-05-12 22:40:26 UTC
Permalink
Post by Faisal Vali
It's also currently a little broken - i think there's a DR that Hubert
recently reported about the invocation of non-trivial functions during
the l-t-rval conversion
Do you mean the paradox of whether *trivial* copy constructors of literal classes implement l-t-r conversion (so the exemption applies), or merely are implemented by l-t-r conversion (so it does not)?

A non-trivial member function would expose a this pointer, rendering the ODR inescapable.
Post by Faisal Vali
- so hopefuly during that rewording, either
the wording becomes clearer or additional illustrative examples will
get added (so please continue to be especially nice to core, so they
don't decide to make it even more convoluted ;)
Yes, this paragraph is a good candidate for the single most obtuse in the standard. And it doesn’t help that it’s not obvious how to write test programs to check for ODR-use.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-discussion/.
1971 powerChina
2015-05-13 01:17:31 UTC
Permalink
Title: The core of the big data solutions -- Map
Author: pengwenwei
Email: ***@foxmail.com
Language: c++
Platform: Windows, linux
Technology: Perfect hash algorithm
Level: Advanced
Description: A high performance map algorithm
Section MFC c++ map stl
SubSection c++ algorithm
License: (GPLv3)


Map is widely used in c++ programs. Its performance is critical to
programs' performance. Especially in big data and the scenarios which
can't realize data distribution and parallel processing.

I have been working on big data analysis for many years in telecommunition
and information security industry. The data analysis is so complicated that
they can't work without map. Especially in information security industry,
the data is much more complicated than others. For example, ip table, mac
table, telephone numbers table, dns table etc.


Currently, the STL map and Google's hash map are the most popular maps. But
they have some disadvantages. The STL map is based on binary chop, which
causes a bad performance. Google Hash map has the best performance at
present, but it has probability of collision. For big data analysis, the
collision probability is unacceptable.

Now I would like to publish pwwMap. It includes three different maps for
different scenarios:
1. Memory Map(memMap): It has a good access speed. But its size is limited
by memory size.
2. Harddisk Map(diskMap): It utilizes hard disk to store data. So it could
accept much more data than memory map.
3. Hashmap(hashMap): It has the best performance and a great lookup speed,
but it doesn't have 'insert' and 'delete' functionality.

MemMap and diskMap could be converted to hashMap by function memMap2HashMap
and diskMap2HashMap. According to the test result, my algorithms' collision
probability is zero. About performance, memMap has a comparable performance
with google, and hashMap's performance is 100 times better than Google's
hashmap.

In summary, pwwhash are perfect hash algorithms with zero collision
probability. You can refer to following artical to find the key index and
compress algorithm theory:
http://blog.csdn.net/chixinmuzi/article/details/1727195

Source code and documents:
https://sourceforge.net/projects/pwwhashmap/files/?source=navbar
Post by Faisal Vali
It's also currently a little broken - i think there's a DR that Hubert
recently reported about the invocation of non-trivial functions during
the l-t-rval conversion
Do you mean the paradox of whether *trivial* copy constructors of literal
classes implement l-t-r conversion (so the exemption applies), or merely
are implemented by l-t-r conversion (so it does not)?
A non-trivial member function would expose a this pointer, rendering the ODR inescapable.
- so hopefuly during that rewording, either
the wording becomes clearer or additional illustrative examples will
get added (so please continue to be especially nice to core, so they
don't decide to make it even more convoluted ;)
Yes, this paragraph is a good candidate for the single most obtuse in the
standard. And it doesn’t help that it’s not obvious how to write test
programs to check for ODR-use.
--
---
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
Visit this group at
http://groups.google.com/a/isocpp.org/group/std-discussion/.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-discussion/.
Faisal Vali
2015-05-13 04:13:14 UTC
Permalink
Post by Faisal Vali
It's also currently a little broken - i think there's a DR that Hubert
recently reported about the invocation of non-trivial functions during
the l-t-rval conversion
Do you mean the paradox of whether *trivial* copy constructors of literal
classes implement l-t-r conversion (so the exemption applies), or merely are
implemented by l-t-r conversion (so it does not)?
Not sure I am aware of this paradox - could I ask you to elaborate?

I was referring to the following examples that Hubert Tong posted on
the core reflector that will be part of DR2083 in the next core issues
list:

<quote>
The resolution of issue 1741 was not intended to cause odr-use to
occur in cases where it did not do so previously. However, in an
example like

extern int globx;
int main() {
const int &x = globx;
struct A {
const int *foo() {
return &x;
}
} a;
return *a.foo();
}

x satisfies the requirements for appearing in a constant expression,
but applying the lvalue-to-rvalue converstion to x does not yield a
constant expression. Similarly,

struct A {
int q;
constexpr A(int q) : q(q) { }
constexpr A(const A &a) : q(a.q * 2) { }
};

int main(void) {
constexpr A a(42);
constexpr int aq = a.q;
struct Q {
int foo() { return a.q; }
} q;
return q.foo();
}

a satisfies the requirements for appearing in a constant expression,
but applying the lvalue-to-rvalue conversion to a invokes a
non-trivial function.

</quote>
Post by Faisal Vali
A non-trivial member function would expose a this pointer, rendering the ODR inescapable.
- so hopefuly during that rewording, either
the wording becomes clearer or additional illustrative examples will
get added (so please continue to be especially nice to core, so they
don't decide to make it even more convoluted ;)
Yes, this paragraph is a good candidate for the single most obtuse in the
standard. And it doesn’t help that it’s not obvious how to write test
programs to check for ODR-use.
Yes - not obvious is right!
Post by Faisal Vali
--
---
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
Visit this group at
http://groups.google.com/a/isocpp.org/group/std-discussion/.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-discussion/.
Belloc
2015-05-13 18:53:54 UTC
Permalink
I believe my first post had a fallacy that I intend to correct now. But
first I'll show that the *e* mentioned in [basic.def.odr]/3 is the whole
expression containing the variable *x* for which we want to determine
whether this variable is odr-used or not. For this, consider the example in
DR 172:

struct S {
static const int a = 1;
static const int b = 2;
};

int f(bool x) { return x ? S::a : S::b; }

I'll use "reductio ad absurdum" to show this. Let's then assume that e = ex = S::a. Then it follows that S::a is a variable that appears as a potentially-evaluated expression ex = S::a, which yields a constant expression that does not evoke a trivial function. Also, S::a is an object, whose expression, ex, is an element of the set of potential results of the expression e = ex, *but* where the lvalue-to-rvalue conversion is *not* applied to *e* (the expression x ? S::a : S::b is an lvalue) and *e* is not a discarded-value expression. We would then conclude that S::a is odr-used, which clearly is an absurd, for DR 172 was put in place to show exactly the opposite, i.e., that the objects S::a and S::b are not odr-used. That means that the *e* mentioned in [basic.def.odr]/3 can not be a (proper) sub-expression of the expression containing the object, for which we want to determine whether or not, it's odr-used.


I'll try to explain now the fallacy in my reasoning in the first post in this discussion. For this I'll rewrite [basic.def.odr]/3, with minor changes (see my emphasis), just to make things clearer, without changing the overall meaning of the paragraph.


A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying the lvalue-to-rvalue conversion (4.1) to x yields a constant expression (5.19) that does not invoke any nontrivial functions and, *for every object **x* that is an element of the set of potential results of an expression e, then either the lvalue-to-rvalue conversion (4.1) is applied to *e*, or *e* is a discarded-value expression (Clause 5).


Now it's clear that the condition "then either the lvalue-to-rvalue conversion (4.1) is applied to *e*, or *e* is a discarded-value expression" has to be satisfied *only* for objects in the expression e that are elements of the set of potential results of *e*. As this set is empty in my first example, this second condition in [basic.def.odr]/3 doesn't need to be satisfied and we conclude that *i* is not odr-used, because it satisfies the first condition above, i.e., that *i* yields a constant expression and does not invoke any non trivial function.


In summary, the paragraph seems to be OK, once we assume that the *e* mentioned in it, is the entire expression containing the variable *x*.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-discussion/.
David Krauss
2015-05-14 02:07:33 UTC
Permalink
Post by Faisal Vali
Post by David Krauss
Do you mean the paradox of whether *trivial* copy constructors of literal
classes implement l-t-r conversion (so the exemption applies), or merely are
implemented by l-t-r conversion (so it does not)?
Not sure I am aware of this paradox - could I ask you to elaborate?
I was thinking of the same issue as DR1741. Maybe it’s better phrased there.

It looks to me like DR1741 also completely handles nontrivial functions. In constexpr function evaluation since C++11, runtime execution is supposed to be viable, but when the ODR forbids using a runtime object the implementation is forced to get the result at compile time.

The phrasing of the resolution seems to point in that direction, to me at least. It would be nice if more rationale were recorded.
Post by Faisal Vali
I was referring to the following examples that Hubert Tong posted on
the core reflector that will be part of DR2083 in the next core issues
The first example is confusing because no l-to-r conversion is ever applied to the expression x. It’s applied to *a.foo(), which isn’t a constant expression because foo isn’t constexpr. If it were constexpr, then constant evaluation would be OK up to that last l-to-r, which fails because there’s no initialization for globx. I seem to be missing the point because it looks straightforward. Also, I don’t see the point of A or a.

The second example is ill-formed because a is not in scope inside Q::foo. Disregarding this, again, no lvalue-to-rvalue conversion is ever applied to a, but only to a.q. If there were an expression invoking the nontrivial copy constructor of A, it should generate an ODR-use compared to C++11 because of the rationale I extrapolated from DR1741. Perhaps Hubert was in the room and they said something about not adding ODR-uses relative to C++11, but such cases were definitely missing.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-discussion/.
Leon Trotski
2015-05-13 18:58:51 UTC
Permalink
I believe my first post had a fallacy that I intend to correct now. But
first I'll show that the *e* mentioned in [basic.def.odr]/3 is the whole
expression containing the variable *x* for which we want to determine
whether this variable is odr-used or not. For this, consider the example in
DR 172:

struct S {
static const int a = 1;
static const int b = 2;
};

int f(bool x) { return x ? S::a : S::b; }

I'll use "reductio ad absurdum" to show this. Let's then assume that e = ex = S::a. Then it follows that S::a is a variable that appears as a potentially-evaluated expression ex = S::a, which yields a constant expression that does not evoke a trivial function. Also, S::a is an object, whose expression, ex, is an element of the set of potential results of the expression e = ex, *but* where the lvalue-to-rvalue conversion is *not* applied to *e* (the expression x ? S::a : S::b is an lvalue) and *e* is not a discarded-value expression. We would then conclude that S::a is odr-used, which clearly is an absurd, for DR 172 was put in place to show exactly the opposite, i.e., that the objects S::a and S::b are not odr-used. That means that the *e* mentioned in [basic.def.odr]/3 can not be a (proper) sub-expression of the expression containing the object, for which we want to determine whether or not, it's odr-used.


I'll try to explain now the fallacy in my reasoning in the first post in this discussion. For this I'll rewrite [basic.def.odr]/3, with minor changes (see my emphasis), just to make things clearer, without changing the overall meaning of the paragraph.


A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying the lvalue-to-rvalue conversion (4.1) to x yields a constant expression (5.19) that does not invoke any nontrivial functions and, *for every object **x* that is an element of the set of potential results of an expression e, then either the lvalue-to-rvalue conversion (4.1) is applied to *e*, or *e* is a discarded-value expression (Clause 5).


Now it's clear that the condition "then either the lvalue-to-rvalue conversion (4.1) is applied to *e*, or *e* is a discarded-value expression" has to be satisfied *only* for objects in the expression e that are elements of the set of potential results of *e*. As this set is empty in my first example, this second condition in [basic.def.odr]/3 doesn't need to be satisfied and we conclude that *i* is not odr-used, because it satisfies the first condition above, i.e., that *i* yields a constant expression and does not invoke any non trivial function.


In summary, the paragraph seems to be OK, once we assume that the *e* mentioned in it, is the entire expression containing the variable *x*.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-discussion/.
dyp
2015-05-13 23:21:23 UTC
Permalink
I think I understand your concern better now, but I don't agree with
your resolution. Here's how I understand [basic.def.odr]p3, in its most
Post by Belloc
struct S {
static const int a = 1;
static const int b = 2;
};
int f(bool x) { return x ? S::a : S::b; }
"A variable x whose name appears as a potentially-evaluated expression
ex is odr-used by ex unless applying the lvalue-to-rvalue conversion
(4.1) to x yields a constant expression (5.20) that does not invoke any
non-trivial functions"

I think we agree on this part: the variables denoted by the names `S::a`
and `S::b` are potentially evaluated in the expression `x ? S::a :
S::b`, and applying the lvalue-to-rvalue conversion to the variables
yields a constant expression that does not invoke any nontrivial functions.


"if x is an object, ex is an element of the set of potential results of
an expression e [...]"

This is where I think we disagree: I interpret this as

If x is an object, an additional condition must be fulfilled to avoid
odr-use: Let S be the set of expressions where ex is a potential result
of. That is, for every expression e in S, ex is a member of the set of
potential results of e. If S contains an element e where either
- the lvalue-to-rvalue conversion is applied to e, or
- e is a discarded-value expression
then x is not odr-used by ex.


Note 0: ex is a specific expression. There can be multiple expressions
all of the form `S::a`, but this paragraph specifically talks about one
instance ex of such an expression.
Note 1: For every element e of the set S, either e is identical with ex,
or ex is a subexpression of e.
Note 2: This (probably) does not cover trivial copy constructors.


Applying this to the example above, to the expression `x ? S::a : S::b`
and the variable denoted by the name `S::a`: The name of the variable
appears as the id-expression `S::a`. The set of expressions where `S::a`
is a member of the potential results is:

S::a // as per 2.1
x ? S::a : S::b // as per 2.6 and 2.1

Of those expressions, neither is a discarded-value expression. But the
lvalue-to-rvalue conversion is (arguably) applied to `x ? S::a : S::b`
in order to initialize the return value of the function f. Therefore,
the variable denoted by `S::a` is not odr-used by the id-expression
`S::a` in this example.

Note that this implies that the function

int const& f(bool x) { return x ? S::a : S::b; }

DOES odr-use `S::a` (and `S::b`), since the lvalue-to-rvalue conversion
is NOT applied to `x ? S::a : S::b` - so there is no element in the set
S that fulfils either condition.


Kind regards,

dyp
Post by Belloc
I believe my first post had a fallacy that I intend to correct now. But
first I'll show that the *e* mentioned in [basic.def.odr]/3 is the whole
expression containing the variable *x* for which we want to determine
whether this variable is odr-used or not. For this, consider the example in
struct S {
static const int a = 1;
static const int b = 2;
};
int f(bool x) { return x ? S::a : S::b; }
I'll use "reductio ad absurdum" to show this. Let's then assume that e = ex = S::a. Then it follows that S::a is a variable that appears as a potentially-evaluated expression ex = S::a, which yields a constant expression that does not evoke a trivial function. Also, S::a is an object, whose expression, ex, is an element of the set of potential results of the expression e = ex, *but* where the lvalue-to-rvalue conversion is *not* applied to *e* (the expression x ? S::a : S::b is an lvalue) and *e* is not a discarded-value expression. We would then conclude that S::a is odr-used, which clearly is an absurd, for DR 172 was put in place to show exactly the opposite, i.e., that the objects S::a and S::b are not odr-used. That means that the *e* mentioned in [basic.def.odr]/3 can not be a (proper) sub-expression of the expression containing the object, for which we want to determine whether or not, it's odr-used.
I'll try to explain now the fallacy in my reasoning in the first post in this discussion. For this I'll rewrite [basic.def.odr]/3, with minor changes (see my emphasis), just to make things clearer, without changing the overall meaning of the paragraph.
A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying the lvalue-to-rvalue conversion (4.1) to x yields a constant expression (5.19) that does not invoke any nontrivial functions and, *for every object **x* that is an element of the set of potential results of an expression e, then either the lvalue-to-rvalue conversion (4.1) is applied to *e*, or *e* is a discarded-value expression (Clause 5).
Now it's clear that the condition "then either the lvalue-to-rvalue conversion (4.1) is applied to *e*, or *e* is a discarded-value expression" has to be satisfied *only* for objects in the expression e that are elements of the set of potential results of *e*. As this set is empty in my first example, this second condition in [basic.def.odr]/3 doesn't need to be satisfied and we conclude that *i* is not odr-used, because it satisfies the first condition above, i.e., that *i* yields a constant expression and does not invoke any non trivial function.
In summary, the paragraph seems to be OK, once we assume that the *e* mentioned in it, is the entire expression containing the variable *x*.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-discussion/.
Richard Smith
2015-05-14 00:24:46 UTC
Permalink
Post by dyp
I think I understand your concern better now, but I don't agree with
your resolution. Here's how I understand [basic.def.odr]p3, in its most
Post by Belloc
struct S {
static const int a = 1;
static const int b = 2;
};
int f(bool x) { return x ? S::a : S::b; }
"A variable x whose name appears as a potentially-evaluated expression
ex is odr-used by ex unless applying the lvalue-to-rvalue conversion
(4.1) to x yields a constant expression (5.20) that does not invoke any
non-trivial functions"
I think we agree on this part: the variables denoted by the names `S::a`
S::b`, and applying the lvalue-to-rvalue conversion to the variables
yields a constant expression that does not invoke any nontrivial functions.
"if x is an object, ex is an element of the set of potential results of
an expression e [...]"
This is where I think we disagree: I interpret this as
If x is an object, an additional condition must be fulfilled to avoid
odr-use: Let S be the set of expressions where ex is a potential result
of. That is, for every expression e in S, ex is a member of the set of
potential results of e. If S contains an element e where either
- the lvalue-to-rvalue conversion is applied to e, or
- e is a discarded-value expression
then x is not odr-used by ex.
Right. Another way to put this:

If x is an object, then consider the set E of expressions (containing ex)
that are either discarded-value expressions or to which the
lvalue-to-rvalue conversion is applied. If there exists an expression e
within E such that ex is in the set of potential results of e, then ex does
not odr-use x.

Note 0: ex is a specific expression. There can be multiple expressions
Post by dyp
all of the form `S::a`, but this paragraph specifically talks about one
instance ex of such an expression.
Note 1: For every element e of the set S, either e is identical with ex,
or ex is a subexpression of e.
Note 2: This (probably) does not cover trivial copy constructors.
Applying this to the example above, to the expression `x ? S::a : S::b`
and the variable denoted by the name `S::a`: The name of the variable
appears as the id-expression `S::a`. The set of expressions where `S::a`
S::a // as per 2.1
x ? S::a : S::b // as per 2.6 and 2.1
Of those expressions, neither is a discarded-value expression. But the
lvalue-to-rvalue conversion is (arguably) applied to `x ? S::a : S::b`
in order to initialize the return value of the function f. Therefore,
the variable denoted by `S::a` is not odr-used by the id-expression
`S::a` in this example.
Note that this implies that the function
int const& f(bool x) { return x ? S::a : S::b; }
DOES odr-use `S::a` (and `S::b`), since the lvalue-to-rvalue conversion
is NOT applied to `x ? S::a : S::b` - so there is no element in the set
S that fulfils either condition.
Kind regards,
dyp
Post by Belloc
I believe my first post had a fallacy that I intend to correct now. But
first I'll show that the *e* mentioned in [basic.def.odr]/3 is the whole
expression containing the variable *x* for which we want to determine
whether this variable is odr-used or not. For this, consider the example
in
Post by Belloc
struct S {
static const int a = 1;
static const int b = 2;
};
int f(bool x) { return x ? S::a : S::b; }
I'll use "reductio ad absurdum" to show this. Let's then assume that e =
ex = S::a. Then it follows that S::a is a variable that appears as a
potentially-evaluated expression ex = S::a, which yields a constant
expression that does not evoke a trivial function. Also, S::a is an object,
whose expression, ex, is an element of the set of potential results of the
expression e = ex, *but* where the lvalue-to-rvalue conversion is *not*
applied to *e* (the expression x ? S::a : S::b is an lvalue) and *e* is not
a discarded-value expression. We would then conclude that S::a is odr-used,
which clearly is an absurd, for DR 172 was put in place to show exactly the
opposite, i.e., that the objects S::a and S::b are not odr-used. That means
that the *e* mentioned in [basic.def.odr]/3 can not be a (proper)
sub-expression of the expression containing the object, for which we want
to determine whether or not, it's odr-used.
Post by Belloc
I'll try to explain now the fallacy in my reasoning in the first post in
this discussion. For this I'll rewrite [basic.def.odr]/3, with minor
changes (see my emphasis), just to make things clearer, without changing
the overall meaning of the paragraph.
Post by Belloc
A variable x whose name appears as a potentially-evaluated expression ex
is odr-used by ex unless applying the lvalue-to-rvalue conversion (4.1) to
x yields a constant expression (5.19) that does not invoke any nontrivial
functions and, *for every object **x* that is an element of the set of
potential results of an expression e, then either the lvalue-to-rvalue
conversion (4.1) is applied to *e*, or *e* is a discarded-value expression
(Clause 5).
Post by Belloc
Now it's clear that the condition "then either the lvalue-to-rvalue
conversion (4.1) is applied to *e*, or *e* is a discarded-value expression"
has to be satisfied *only* for objects in the expression e that are
elements of the set of potential results of *e*. As this set is empty in my
first example, this second condition in [basic.def.odr]/3 doesn't need to
be satisfied and we conclude that *i* is not odr-used, because it satisfies
the first condition above, i.e., that *i* yields a constant expression and
does not invoke any non trivial function.
Post by Belloc
In summary, the paragraph seems to be OK, once we assume that the *e*
mentioned in it, is the entire expression containing the variable *x*.
--
---
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
Visit this group at
http://groups.google.com/a/isocpp.org/group/std-discussion/.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-discussion/.
Leon Trotski
2015-05-14 15:11:39 UTC
Permalink
You wrote this and I think I understood what you meant.

If x is an object, an additional condition must be fulfilled to
avoid odr-use: Let S be the set of expressions where ex is a potential
result of. That is, for every expression e in S, ex is a member of the set
of potential results of e. If S contains an element e where either
- the lvalue-to-rvalue conversion is applied to e, or
- e is a discarded-value expression
then x is not odr-used by ex.

You're saying that if we can show *just one* element of the set S that
satisfies one of the conditions above, then the object x is not odr-used
(assuming of course that x satisfies the first condition stated in the
paragraph, i.e., that it is a constant expression). I just can't see how
can you reach this conclusion (your interpretation of the text), just by
reading the Standard?

But even if we accept this interpretation as valid, there's still the
problem you mentioned on your first comment above, about the fact that the
Standard doesn't seem to support the lvalue-to-rvalue conversion of the
variable *i* in my first example when it's passed by-value to the function
*f*, which is a necessary condition, according to your interpretation, for
this variable to be considered not odr-used by the expression *f(i)*.
Post by dyp
I think I understand your concern better now, but I don't agree with
your resolution. Here's how I understand [basic.def.odr]p3, in its most
Post by Belloc
struct S {
static const int a = 1;
static const int b = 2;
};
int f(bool x) { return x ? S::a : S::b; }
"A variable x whose name appears as a potentially-evaluated expression
ex is odr-used by ex unless applying the lvalue-to-rvalue conversion
(4.1) to x yields a constant expression (5.20) that does not invoke any
non-trivial functions"
I think we agree on this part: the variables denoted by the names `S::a`
S::b`, and applying the lvalue-to-rvalue conversion to the variables
yields a constant expression that does not invoke any nontrivial functions.
"if x is an object, ex is an element of the set of potential results of
an expression e [...]"
This is where I think we disagree: I interpret this as
If x is an object, an additional condition must be fulfilled to avoid
odr-use: Let S be the set of expressions where ex is a potential result
of. That is, for every expression e in S, ex is a member of the set of
potential results of e. If S contains an element e where either
- the lvalue-to-rvalue conversion is applied to e, or
- e is a discarded-value expression
then x is not odr-used by ex.
Note 0: ex is a specific expression. There can be multiple expressions
all of the form `S::a`, but this paragraph specifically talks about one
instance ex of such an expression.
Note 1: For every element e of the set S, either e is identical with ex,
or ex is a subexpression of e.
Note 2: This (probably) does not cover trivial copy constructors.
Applying this to the example above, to the expression `x ? S::a : S::b`
and the variable denoted by the name `S::a`: The name of the variable
appears as the id-expression `S::a`. The set of expressions where `S::a`
S::a // as per 2.1
x ? S::a : S::b // as per 2.6 and 2.1
Of those expressions, neither is a discarded-value expression. But the
lvalue-to-rvalue conversion is (arguably) applied to `x ? S::a : S::b`
in order to initialize the return value of the function f. Therefore,
the variable denoted by `S::a` is not odr-used by the id-expression
`S::a` in this example.
Note that this implies that the function
int const& f(bool x) { return x ? S::a : S::b; }
DOES odr-use `S::a` (and `S::b`), since the lvalue-to-rvalue conversion
is NOT applied to `x ? S::a : S::b` - so there is no element in the set
S that fulfils either condition.
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-discussion/.
dyp
2015-05-15 01:01:06 UTC
Permalink
As far as I understand, the [basic.def.odr]p3 tries to codify in
Standardese the following concept:

Objects are regions of storage and can contain values. Let x be an
object, and ex be an expression that names this object. If the compiler
knows the value stored within x at the point where ex is evaluated, it
does not need to read this value from the object. Therefore, if we only
use ex to read the value of x, the object (the region of storage) x does
not need to exist. Similarly, if the compiler knows that the value of ex
is discarded, the region of storage x does not need to exist.

This has been restricted to values known at compile-time; specifically
to values that all conforming compilers must be able to compute (know)
at compile-time. This restriction is probably for portability.

Another way to formulate the concept: If we do not need the address of
x, and if we do not need to store something within x, then we do not
need a region of storage (with a unique address).

There is an additional restriction: The value of x has to be read
"immediately" from ex. For example:

{
auto* p = &ex;
auto c = *p; // indirect read
}

I would guess this too is to keep things simple for implementers, and
therefore allow practical portability. Imagine a less restrictive rule,
where some compilers require a definition of x in the above example
because they're unable to determine that p is only used to read the
value of x. Consider a situation where the address of x is required:

auto* p = &ex;
std::cout << (void*)p; // unique address is guaranteed

Here, we do need the region of storage x for its unique address.

The lvalue-to-rvalue conversion reads the value of an object (lvalues
are always objects). So when the lvalue-to-rvalue conversion is applied
directly to ex, and ex is a compile-time constant, we do not need the
object x (we do not odr-use x). In other contexts, the object x can be
required. The definition of x is NOT required if it is NEVER odr-used.

If the l-t-r conv is not directly applied to ex, maybe it is applied to
an expression that ex is part of? Assume ex and ey are of type const int:

42 + (b ? ex : ey)

The l-t-r conversion is not applied directly to ex, but it is not very
hard for the compiler to see that in order to compute the value of the
complete expression, we only need the value of ex, but not the object x.
This is what CWG 712 is about: There are cases that are simple enough
that do not want to enforce the existence of a definition of x, even
though they do not immediately read the value of x via the expression ex.

These cases have been specified by looking at the (sub)expressions e
that contain ex. We start from ex and move towards the complete
expression c. If we can easily determine that e either discards the
value of x or only reads the value of x, then we're done: we know that
this occurrence of ex does not require an object x. Otherwise, we look
at the expression e' that e is part of, and ask again: can we easily
determine that e' reads from x or discard its value?
We will end up either at an expression that is too complicated (so we
require a definition), or we have already determined that we don't need
x here.
Post by Leon Trotski
I just can't see how
can you reach this conclusion (your interpretation of the text), just
by reading the Standard?
The Standard says: "ex is an element of the set of potential results of
an expression e [, where e fulfils the following criteria ...]"
If ex is an element of the set of potential results of ANY such
expression e - call it e' - than this condition is fulfilled:

ex is an element of the set of potential results of the expression e',
and e' fulfils the criteria

The Standard doesn't say "[...] of a complete expression e", so we are
potentially looking at subexpressions. Additionally, if ALL expressions
that ex is part of had to fulfil the criteria, then it should read "ex
is an element of the set of potential results of ALL expressions e it is
part of". So I concluded: just one such expression e is sufficient.


On the topic of initialization vs lvalue-to-rvalue conversion: Some time
ago (C++11), I would have said that some forms of initialization are
supposed to apply the lvalue-to-rvalue conversion, such as:

int x;
int y = x; // causes l-t-r

because the l-t-r at that time was the only place where it said that
reading from an uninitialized object causes Undefined Behaviour. But
with the resolution of CWG 1787, this has changed:

https://github.com/cplusplus/draft/commit/426888a7fdd7d8ee6aa1aef60f1f5e3526faaf6c

Now, the UB in the above example is introduced by _an evaluation that
produces an indeterminate value_. The lvalue-to-rvalue conversion is no
longer required for the UB. We do know from [dcl.init]p17.8 that we need
to extract the value from x, but as far as I know, the Standard does not
strictly require the lvalue-to-rvalue conversion to read the value from
an object.

This missing piece otherwise seemed to be purely a problem for
language-lawyers, but now it seems that [basic.def.odr]p3 requires this
missing piece in order to codify the concept I've mentioned above: if
these kinds of initialization do not require l-t-r conversion, then:

struct S {
constexpr static short M = 42;
};

int x = M; // odr-use of M
int y = 0 + M; // no odr-use of M


And this, to me, does not seem to be the intention of [basic.def.odr]p3.



Kind regards,

dyp
Post by Leon Trotski
You wrote this and I think I understood what you meant.
If x is an object, an additional condition must be fulfilled to
avoid odr-use: Let S be the set of expressions where ex is a potential
result of. That is, for every expression e in S, ex is a member of the set
of potential results of e. If S contains an element e where either
- the lvalue-to-rvalue conversion is applied to e, or
- e is a discarded-value expression
then x is not odr-used by ex.
You're saying that if we can show *just one* element of the set S that
satisfies one of the conditions above, then the object x is not odr-used
(assuming of course that x satisfies the first condition stated in the
paragraph, i.e., that it is a constant expression). I just can't see how
can you reach this conclusion (your interpretation of the text), just by
reading the Standard?
But even if we accept this interpretation as valid, there's still the
problem you mentioned on your first comment above, about the fact that the
Standard doesn't seem to support the lvalue-to-rvalue conversion of the
variable *i* in my first example when it's passed by-value to the function
*f*, which is a necessary condition, according to your interpretation, for
this variable to be considered not odr-used by the expression *f(i)*.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-discussion/.
Leon Trotski
2015-05-15 19:18:50 UTC
Permalink
This was an outstanding answer and I want to thank you for your patience in
clarifying all the small details that I was concerned with in relation to
[basic.def.odr]/3. Also, I want to congratulate you on the quality
and rigor of all your comments on this discussion.

But there's still one thing that I'm missing: why don't you (or anyboby
else) submit an issue to CWG about the necessity of the l-t-r conversion,
for the cases shown below (and above)?
Post by dyp
As far as I understand, the [basic.def.odr]p3 tries to codify in
Objects are regions of storage and can contain values. Let x be an
object, and ex be an expression that names this object. If the compiler
knows the value stored within x at the point where ex is evaluated, it
does not need to read this value from the object. Therefore, if we only
use ex to read the value of x, the object (the region of storage) x does
not need to exist. Similarly, if the compiler knows that the value of ex
is discarded, the region of storage x does not need to exist.
This has been restricted to values known at compile-time; specifically
to values that all conforming compilers must be able to compute (know)
at compile-time. This restriction is probably for portability.
Another way to formulate the concept: If we do not need the address of
x, and if we do not need to store something within x, then we do not
need a region of storage (with a unique address).
There is an additional restriction: The value of x has to be read
{
auto* p = &ex;
auto c = *p; // indirect read
}
I would guess this too is to keep things simple for implementers, and
therefore allow practical portability. Imagine a less restrictive rule,
where some compilers require a definition of x in the above example
because they're unable to determine that p is only used to read the
auto* p = &ex;
std::cout << (void*)p; // unique address is guaranteed
Here, we do need the region of storage x for its unique address.
The lvalue-to-rvalue conversion reads the value of an object (lvalues
are always objects). So when the lvalue-to-rvalue conversion is applied
directly to ex, and ex is a compile-time constant, we do not need the
object x (we do not odr-use x). In other contexts, the object x can be
required. The definition of x is NOT required if it is NEVER odr-used.
If the l-t-r conv is not directly applied to ex, maybe it is applied to
42 + (b ? ex : ey)
The l-t-r conversion is not applied directly to ex, but it is not very
hard for the compiler to see that in order to compute the value of the
complete expression, we only need the value of ex, but not the object x.
This is what CWG 712 is about: There are cases that are simple enough
that do not want to enforce the existence of a definition of x, even
though they do not immediately read the value of x via the expression ex.
These cases have been specified by looking at the (sub)expressions e
that contain ex. We start from ex and move towards the complete
expression c. If we can easily determine that e either discards the
value of x or only reads the value of x, then we're done: we know that
this occurrence of ex does not require an object x. Otherwise, we look
at the expression e' that e is part of, and ask again: can we easily
determine that e' reads from x or discard its value?
We will end up either at an expression that is too complicated (so we
require a definition), or we have already determined that we don't need
x here.
Post by Leon Trotski
I just can't see how
can you reach this conclusion (your interpretation of the text), just
by reading the Standard?
The Standard says: "ex is an element of the set of potential results of
an expression e [, where e fulfils the following criteria ...]"
If ex is an element of the set of potential results of ANY such
ex is an element of the set of potential results of the expression e',
and e' fulfils the criteria
The Standard doesn't say "[...] of a complete expression e", so we are
potentially looking at subexpressions. Additionally, if ALL expressions
that ex is part of had to fulfil the criteria, then it should read "ex
is an element of the set of potential results of ALL expressions e it is
part of". So I concluded: just one such expression e is sufficient.
On the topic of initialization vs lvalue-to-rvalue conversion: Some time
ago (C++11), I would have said that some forms of initialization are
int x;
int y = x; // causes l-t-r
because the l-t-r at that time was the only place where it said that
reading from an uninitialized object causes Undefined Behaviour. But
https://github.com/cplusplus/draft/commit/426888a7fdd7d8ee6aa1aef60f1f5e3526faaf6c
Now, the UB in the above example is introduced by _an evaluation that
produces an indeterminate value_. The lvalue-to-rvalue conversion is no
longer required for the UB. We do know from [dcl.init]p17.8 that we need
to extract the value from x, but as far as I know, the Standard does not
strictly require the lvalue-to-rvalue conversion to read the value from
an object.
This missing piece otherwise seemed to be purely a problem for
language-lawyers, but now it seems that [basic.def.odr]p3 requires this
missing piece in order to codify the concept I've mentioned above: if
struct S {
constexpr static short M = 42;
};
int x = M; // odr-use of M
int y = 0 + M; // no odr-use of M
And this, to me, does not seem to be the intention of [basic.def.odr]p3.
Kind regards,
dyp
Post by Leon Trotski
You wrote this and I think I understood what you meant.
If x is an object, an additional condition must be fulfilled to
avoid odr-use: Let S be the set of expressions where ex is a potential
result of. That is, for every expression e in S, ex is a member of the
set
Post by Leon Trotski
of potential results of e. If S contains an element e where either
- the lvalue-to-rvalue conversion is applied to e, or
- e is a discarded-value expression
then x is not odr-used by ex.
You're saying that if we can show *just one* element of the set S that
satisfies one of the conditions above, then the object x is not odr-used
(assuming of course that x satisfies the first condition stated in the
paragraph, i.e., that it is a constant expression). I just can't see how
can you reach this conclusion (your interpretation of the text), just by
reading the Standard?
But even if we accept this interpretation as valid, there's still the
problem you mentioned on your first comment above, about the fact that
the
Post by Leon Trotski
Standard doesn't seem to support the lvalue-to-rvalue conversion of the
variable *i* in my first example when it's passed by-value to the
function
Post by Leon Trotski
*f*, which is a necessary condition, according to your interpretation,
for
Post by Leon Trotski
this variable to be considered not odr-used by the expression *f(i)*.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-discussion/.
Leon Trotski
2015-05-17 22:04:44 UTC
Permalink
Paragraph §4/3 and §4/6 in C++14, don't they answer your concern about the
possible lack of a specification for an lvalue-to-rvalue conversion, when
built-in types are initialized, as you mentioned below?
Post by dyp
On the topic of initialization vs lvalue-to-rvalue conversion: Some time
ago (C++11), I would have said that some forms of initialization are
int x;
int y = x; // causes l-t-r
because the l-t-r at that time was the only place where it said that
reading from an uninitialized object causes Undefined Behaviour. But
https://github.com/cplusplus/draft/commit/426888a7fdd7d8ee6aa1aef60f1f5e3526faaf6c
Now, the UB in the above example is introduced by _an evaluation that
produces an indeterminate value_. The lvalue-to-rvalue conversion is no
longer required for the UB. We do know from [dcl.init]p17.8 that we need
to extract the value from x, but as far as I know, the Standard does not
strictly require the lvalue-to-rvalue conversion to read the value from
an object.
This missing piece otherwise seemed to be purely a problem for
language-lawyers, but now it seems that [basic.def.odr]p3 requires this
missing piece in order to codify the concept I've mentioned above: if
struct S {
constexpr static short M = 42;
};
int x = M; // odr-use of M
int y = 0 + M; // no odr-use of M
And this, to me, does not seem to be the intention of [basic.def.odr]p3.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-discussion/.
dyp
2015-05-17 23:52:41 UTC
Permalink
To me, [conv]p3 and p6 aren't explicit enough to conclude that
initialization in those cases requires an l-t-r conversion. For example,
p6 says "The effect of any implicit conversion is the same as performing
the corresponding declaration and initialization and then using the
temporary variable as the result of the conversion". But we're not using
the initialized variable in an expression when simply initializing it:

int x = 42;
int y = x; // y is not used!

So there is no expression that could be a prvalue.

There are other hints that l-t-r should be applied, e.g. the value
categories [basic.lval]p1 imply that only prvalues can be "values".

C11 (N1570) has the similar lvalue conversion in §6.3.2.1p2:

Except when it is the operand of the sizeof operator, the _Alignof
operator, the unary & operator, the ++ operator, the -- operator, or the
left operand of the . operator or an assignment operator, an lvalue that
does not have array type is converted to the value stored in the
designated object (and is no longer an lvalue); this is called lvalue
conversion.

However, in C++ the situation might be more complicated, as David
Krauss' remark shows. I came up with the following related issue:

struct trivial
{
trivial() = default;
trivial(trivial const&) = default; // or even trivial&
trivial(trivial&&) = delete;
};
struct nontrivial
{
nontrivial() = default;
nontrivial(nontrivial const&) {}
};

struct enclosing
{
static constexpr int i = {};
static constexpr trivial t = {};
static constexpr nontrivial n = {};
};

auto j = enclosing::i; // l-t-r applied?
auto u = enclosing::t; // l-t-r applied?
auto m = enclosing::n; // l-t-r applied?

In second initialization `auto u = ..`, a trivial copy constructor is
called. I don't see how an l-t-r conversion could apply here - I can
only think that this kind of initialization is considered _equivalent_
to an lvalue-to-rvalue conversion, even though the initialization target
is not a temporary.

Neither clang++ nor g++ require a definition for i nor t, but both
require a definition for n (this looks like CWG 1741).


Additionally, there is the issue of CWG 1642, and this SO discussion on
the topic:

http://stackoverflow.com/q/14935722/

The second answer (by Johannes Schaub) seems to use the same approach
you're suggesting, while the first answer (by Andy Prowl) uses a lot of
reading between the lines, conjectures etc. It's just too vague for me
to be a definite answer. Something this simple should be easy to
understand completely and easy to explain. Even if everyone agrees that
the l-t-r is applied in those cases, I would appreciate some clarification.


Kind regards,

dyp
Paragraph §4/3 and §4/6 in C++14, don't they answer your concern about the
possible lack of a specification for an lvalue-to-rvalue conversion, when
built-in types are initialized, as you mentioned below?
Post by dyp
On the topic of initialization vs lvalue-to-rvalue conversion: Some time
ago (C++11), I would have said that some forms of initialization are
int x;
int y = x; // causes l-t-r
because the l-t-r at that time was the only place where it said that
reading from an uninitialized object causes Undefined Behaviour. But
https://github.com/cplusplus/draft/commit/426888a7fdd7d8ee6aa1aef60f1f5e3526faaf6c
Now, the UB in the above example is introduced by _an evaluation that
produces an indeterminate value_. The lvalue-to-rvalue conversion is no
longer required for the UB. We do know from [dcl.init]p17.8 that we need
to extract the value from x, but as far as I know, the Standard does not
strictly require the lvalue-to-rvalue conversion to read the value from
an object.
This missing piece otherwise seemed to be purely a problem for
language-lawyers, but now it seems that [basic.def.odr]p3 requires this
missing piece in order to codify the concept I've mentioned above: if
struct S {
constexpr static short M = 42;
};
int x = M; // odr-use of M
int y = 0 + M; // no odr-use of M
And this, to me, does not seem to be the intention of [basic.def.odr]p3.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-discussion/.
Leon Trotski
2015-05-18 17:42:27 UTC
Permalink
Thanks for your reply. You said:

"Neither clang++ nor g++ require a definition for i nor t, but both
require a definition for n (this looks like CWG 1741)."

For the C++14 version of the compilers a definition is not required for i,
t and n. See this example
<http://coliru.stacked-crooked.com/a/57b48d704b733ead>.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-discussion/.
dyp
2015-05-18 17:47:15 UTC
Permalink
I was surprised for a moment, since I did check this on coliru before
sending my prior mail. The difference between your test and mine is the
optimization level. If you turn off optimizations, both coliru's clang++
and g++ do report a linker error: a missing definition of enclosing::n

(Violations of the ODR are ill-formed with No Diagnostic Required.)


Kind regards,

dyp
Post by Leon Trotski
"Neither clang++ nor g++ require a definition for i nor t, but both
require a definition for n (this looks like CWG 1741)."
For the C++14 version of the compilers a definition is not required for i,
t and n. See this example
<http://coliru.stacked-crooked.com/a/57b48d704b733ead>.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-discussion/.
Leon Trotski
2015-05-13 19:04:47 UTC
Permalink
I'm having some problem submitting a new post in this discussion. I ask you
the to see this document
<https://docs.google.com/document/d/1g_0uOtirtKj0OKKBXlMkOJpmjZ2tXTaL3-oUZ7coByc/edit?usp=sharing>
in My Google Drive.
--
---
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-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-discussion/.
Loading...