Discussion:
side effects of moving standard types
(too old to reply)
Nathan Ernst
2015-04-17 01:02:34 UTC
Permalink
(This question is based upon document ISO/IEC 14882:2011(E))

I'm writing a parser that's going to end up generating lots of instances of
standard strings and vectors, and I'm trying to be as unobtrusive in terms
of memory as I can. I'm trying to move the generated strings and vectors
for obvious reasons, but it got me thinking about the state of the the
carcass of the moved instance.

For strings, I found the language in § 21.4.2 paragraph 2 that "In the
second form, str is left in a valid state with an unspecified value." The
second form refers to the the rvalue-reference constructor of basic_string.

From this language, I expect I should be able to call methods on a
previously moved instance without introducing undefined behavior, but the
result of those calls are unspecified (calling c_str could return an empty
string or anything else). What I gather from this is that if I wanted to
reuse a previously moved string instance, I would need re-initialize it in
order to get to a known state.

As an example, if I wrote:

std::function<void(std::string&&)> sink = ...;
std::string s;

for (int i = 0; i < 26; ++i)
{
s.append('a' + i);
sink(std::move(s));
}


It would be conformant for sink to receive "a", "b", "c".... or to receive
"a", "ab", "abc", or really anything else after initially receiving "a".

Question boils down to: To be in a known & valid state, do I need to
reinitialize a moved type in order to be able to guarantee behavior?

i.e. Do I need to do the following in order to ensure sink receives "a",
"b", "c"...:

std::function<void(std::string&&)> sink = ...;
std::string s;

for (int i = 0; i < 26; ++i)
{
s.append('a' + i);
sink(std::move(s));

s = std::string(); // possibly redundant?

}

In general would this assumption hold across all standard movable types? Is
the extra re-initialization necessary to ensure cross
platform/implementation behavior?

Thanks,
Nate
--
---
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-04-17 01:17:59 UTC
Permalink
Post by Nathan Ernst
(This question is based upon document ISO/IEC 14882:2011(E))
I'm writing a parser that's going to end up generating lots of instances
of standard strings and vectors, and I'm trying to be as unobtrusive in
terms of memory as I can. I'm trying to move the generated strings and
vectors for obvious reasons, but it got me thinking about the state of the
the carcass of the moved instance.
For strings, I found the language in § 21.4.2 paragraph 2 that "In the
second form, str is left in a valid state with an unspecified value." The
second form refers to the the rvalue-reference constructor of basic_string.
From this language, I expect I should be able to call methods on a
previously moved instance without introducing undefined behavior, but the
result of those calls are unspecified (calling c_str could return an empty
string or anything else). What I gather from this is that if I wanted to
reuse a previously moved string instance, I would need re-initialize it in
order to get to a known state.
std::function<void(std::string&&)> sink = ...;
std::string s;
for (int i = 0; i < 26; ++i)
{
s.append('a' + i);
sink(std::move(s));
}
It would be conformant for sink to receive "a", "b", "c".... or to receive
"a", "ab", "abc", or really anything else after initially receiving "a".
Question boils down to: To be in a known & valid state, do I need to
reinitialize a moved type in order to be able to guarantee behavior?
"reinitialize" isn't the best word (because you would not do this via
initialization), but yes, you need to set the value of the object (through
assignment, x.clear(), or similar).

i.e. Do I need to do the following in order to ensure sink receives "a",
Post by Nathan Ernst
std::function<void(std::string&&)> sink = ...;
std::string s;
for (int i = 0; i < 26; ++i)
{
s.append('a' + i);
sink(std::move(s));
s = std::string(); // possibly redundant?
}
In general would this assumption hold across all standard movable types?
Yes, except for library types that guarantee that a moved-from object has a
specific value (a notable exception is unique_ptr, which is guaranteed to
be null when it has been moved from).
Post by Nathan Ernst
Is the extra re-initialization necessary to ensure cross
platform/implementation behavior?
Yes. For instance, some implementations use a small string optimization for
std::string, and it is common for moving from a string in its "small" state
to leave the original string's value alone, which could result in calling
your sink with arguments "a", "ab", "abc", "abcd", ..., "abcdefgh", "i",
"j", ... for instance (for an 8 byte SSO buffer).
--
---
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/.
Nathan Ernst
2015-04-17 01:50:01 UTC
Permalink
Got it, Richard. Appreciate the response, and, unfortunately, confirming my
fears. The small string example really helped to make it clear why.

Regards,
Nate
Post by Richard Smith
Post by Nathan Ernst
(This question is based upon document ISO/IEC 14882:2011(E))
I'm writing a parser that's going to end up generating lots of instances
of standard strings and vectors, and I'm trying to be as unobtrusive in
terms of memory as I can. I'm trying to move the generated strings and
vectors for obvious reasons, but it got me thinking about the state of the
the carcass of the moved instance.
For strings, I found the language in § 21.4.2 paragraph 2 that "In the
second form, str is left in a valid state with an unspecified value." The
second form refers to the the rvalue-reference constructor of basic_string.
From this language, I expect I should be able to call methods on a
previously moved instance without introducing undefined behavior, but the
result of those calls are unspecified (calling c_str could return an empty
string or anything else). What I gather from this is that if I wanted to
reuse a previously moved string instance, I would need re-initialize it in
order to get to a known state.
std::function<void(std::string&&)> sink = ...;
std::string s;
for (int i = 0; i < 26; ++i)
{
s.append('a' + i);
sink(std::move(s));
}
It would be conformant for sink to receive "a", "b", "c".... or to
receive "a", "ab", "abc", or really anything else after initially receiving
"a".
Question boils down to: To be in a known & valid state, do I need to
reinitialize a moved type in order to be able to guarantee behavior?
"reinitialize" isn't the best word (because you would not do this via
initialization), but yes, you need to set the value of the object (through
assignment, x.clear(), or similar).
i.e. Do I need to do the following in order to ensure sink receives "a",
Post by Nathan Ernst
std::function<void(std::string&&)> sink = ...;
std::string s;
for (int i = 0; i < 26; ++i)
{
s.append('a' + i);
sink(std::move(s));
s = std::string(); // possibly redundant?
}
In general would this assumption hold across all standard movable types?
Yes, except for library types that guarantee that a moved-from object has
a specific value (a notable exception is unique_ptr, which is guaranteed to
be null when it has been moved from).
Post by Nathan Ernst
Is the extra re-initialization necessary to ensure cross
platform/implementation behavior?
Yes. For instance, some implementations use a small string optimization
for std::string, and it is common for moving from a string in its "small"
state to leave the original string's value alone, which could result in
calling your sink with arguments "a", "ab", "abc", "abcd", ..., "abcdefgh",
"i", "j", ... for instance (for an 8 byte SSO buffer).
--
---
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/.
Nevin Liber
2015-04-17 01:52:56 UTC
Permalink
Post by Nathan Ernst
Got it, Richard. Appreciate the response, and, unfortunately, confirming
my fears. The small string example really helped to make it clear why.
If you are that concerned, you can always create your own string type that
wraps std::string with a move constructor / assignment that clear()s the
source afterwards.
--
Nevin ":-)" Liber <mailto:***@eviloverlord.com> (847) 691-1404
--
---
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/.
Nathan Ernst
2015-04-17 02:17:36 UTC
Permalink
'Fear' was probably a too strong and wrong word to use, yet Richard's
explanation confirmed that I shouldn't rely upon observed behavior of one
compiler.
Post by Nevin Liber
Post by Nathan Ernst
Got it, Richard. Appreciate the response, and, unfortunately, confirming
my fears. The small string example really helped to make it clear why.
If you are that concerned, you can always create your own string type that
wraps std::string with a move constructor / assignment that clear()s the
source afterwards.
--
--
---
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/.
g***@gmail.com
2015-04-22 01:44:07 UTC
Permalink
Post by Richard Smith
Post by Nathan Ernst
(This question is based upon document ISO/IEC 14882:2011(E))
I'm writing a parser that's going to end up generating lots of instances
of standard strings and vectors, and I'm trying to be as unobtrusive in
terms of memory as I can. I'm trying to move the generated strings and
vectors for obvious reasons, but it got me thinking about the state of the
the carcass of the moved instance.
For strings, I found the language in § 21.4.2 paragraph 2 that "In the
second form, str is left in a valid state with an unspecified value." The
second form refers to the the rvalue-reference constructor of basic_string.
From this language, I expect I should be able to call methods on a
previously moved instance without introducing undefined behavior, but the
result of those calls are unspecified (calling c_str could return an empty
string or anything else). What I gather from this is that if I wanted to
reuse a previously moved string instance, I would need re-initialize it in
order to get to a known state.
std::function<void(std::string&&)> sink = ...;
std::string s;
for (int i = 0; i < 26; ++i)
{
s.append('a' + i);
sink(std::move(s));
}
It would be conformant for sink to receive "a", "b", "c".... or to
receive "a", "ab", "abc", or really anything else after initially receiving
"a".
Question boils down to: To be in a known & valid state, do I need to
reinitialize a moved type in order to be able to guarantee behavior?
"reinitialize" isn't the best word (because you would not do this via
initialization), but yes, you need to set the value of the object (through
assignment, x.clear(), or similar).
i.e. Do I need to do the following in order to ensure sink receives "a",
Post by Nathan Ernst
std::function<void(std::string&&)> sink = ...;
std::string s;
for (int i = 0; i < 26; ++i)
{
s.append('a' + i);
sink(std::move(s));
s = std::string(); // possibly redundant?
}
In general would this assumption hold across all standard movable types?
Yes, except for library types that guarantee that a moved-from object has
a specific value (a notable exception is unique_ptr, which is guaranteed to
be null when it has been moved from).
Post by Nathan Ernst
Is the extra re-initialization necessary to ensure cross
platform/implementation behavior?
Yes. For instance, some implementations use a small string optimization
for std::string, and it is common for moving from a string in its "small"
state to leave the original string's value alone, which could result in
calling your sink with arguments "a", "ab", "abc", "abcd", ..., "abcdefgh",
"i", "j", ... for instance (for an 8 byte SSO buffer).
So I'm absolutely clear on this subject, on my machine this program:

#include <string>
#include <cassert>
#include <cstdio>
void f(std::string&& s);
int main()
{
std::string s = "hello";
f(std::move(s));
std::printf("main() s: \"%s\"\n", s.c_str());
std::printf("main() empty: %s\n", s.empty() ? "true" : "false");
}
void f(std::string&& s)
{
std::string s2 = std::move(s);
}

// on Windows with clang and libc++, the output is:
main() s: ""
main() empty: true

Are you saying that this output is undefined, i.e. that on another machine
the output might be something different?
Such as:
main() s: "hello"
main() empty: false
--
---
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/.
Nevin Liber
2015-04-22 01:51:55 UTC
Permalink
Post by g***@gmail.com
Are you saying that this output is undefined, i.e. that on another machine
the output might be something different?
Yes. Not quite undefined, but in a valid but unspecified state.

From n4431 [defns.valid]:
valid but unspecified state

an object state that is not specified except that the object’s invariants
are met and operations on the object behave as specified for its type

[ Example: If an object x of type std::vector<int> is in a valid but
unspecified state, x.empty() can be called unconditionally, and x.front()
can be called only if x.empty() returns false. —end example ]
--
Nevin ":-)" Liber <mailto:***@eviloverlord.com> (847) 691-1404
--
---
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/.
g***@gmail.com
2015-04-22 04:16:17 UTC
Permalink
Post by Nevin Liber
Post by g***@gmail.com
Are you saying that this output is undefined, i.e. that on another
machine the output might be something different?
Yes. Not quite undefined, but in a valid but unspecified state.
valid but unspecified state
an object state that is not specified except that the object’s invariants
are met and operations on the object behave as specified for its type
[ Example: If an object x of type std::vector<int> is in a valid but
unspecified state, x.empty() can be called unconditionally, and x.front()
can be called only if x.empty() returns false. —end example ]
--
691-1404
I have seen a lot of code like that, which is technically broken then. I've
written code like that myself regarding string.
I think perhaps the standard should change to make such code work as
expected for things like string.

e.g.: guarantee this assert will never fail.

std::string s1 = "hello";
std::string s2 = std::move(s)
assert(s1.empty());

What says the people?
--
---
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/.
Francis (Grizzly) Smit
2015-04-22 04:20:53 UTC
Permalink
Post by g***@gmail.com
Are you saying that this output is undefined, i.e. that on
another machine the output might be something different?
Yes. Not quite undefined, but in a valid but unspecified state.
valid but unspecified state
an object state that is not specified except that the object’s
invariants are met and operations on the object behave as
specified for its type
[ Example: If an object x of type std::vector<int> is in a valid
but unspecified state, x.empty() can be called unconditionally,
and x.front() can be called only if x.empty() returns false. —end
example ]
--
(847) 691-1404
I have seen a lot of code like that, which is technically broken then.
I've written code like that myself regarding string.
I think perhaps the standard should change to make such code work as
expected for things like string.
e.g.: guarantee this assert will never fail.
std::string s1 = "hello";
std::string s2 = std::move(s)
assert(s1.empty());
What says the people?
what make the compiler deduce that s is s1 ??? not a good idea if it's
even doable
Post by g***@gmail.com
--
---
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
Visit this group at
http://groups.google.com/a/isocpp.org/group/std-discussion/.
--
.~. In my life God comes first....
/V\ but Linux is pretty high after that :-D
/( )\ Francis (Grizzly) Smit
^^-^^ http://www.smit.id.au/
-----BEGIN GEEK CODE BLOCK-----
Version: 3.1
GM/CS/H/P/S/IT/L d- s+:+ a++ C++++ UL++++$ P++ L+++$ E--- W++
N W--- M-- V-- PE- PGP t+ 5-- X-- R- tv b++++ D-
G e++ h+ y?
------END GEEK CODE BLOCK------
http://www.geekcode.com/
--
---
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 Rodríguez Ibeas
2015-04-22 07:00:48 UTC
Permalink
Post by g***@gmail.com
I have seen a lot of code like that, which is technically broken then.
I've written code like that myself regarding string.
I think perhaps the standard should change to make such code work as
expected for things like string.
I have seen a lot of people expecting wrong programs to behave correctly,
that does not mean that the language has to adjust to match the
expectations of those people, rather that those people need to learn what
can and cannot be done within the bounds of the language.

People expects arrays and pointers to be the same, believe that
dereferencing null is fine as long as you immediately take the address of
the lvalue or a member of it, you can do network-to-host of a double in
place through reinterpret_cast<int64_t*> and ntohll... should all those be
adjusted to the expectations of users?

Expecting that the resulting string is empty might not be a big change, but
the moved-from object would have to undertake two modifications that are
not needed today: reset the size to 0, set the first character in the
buffer to 0. Neither of those operations help the cause of moving from a
real rvalue and feel a bit like setting a pointer to nullptr after deleting
the object in the destructor, not really that useful. If you really want
the string to be cleared you have the option of paying for the operation
yourself, without forcing all other uses onto that cost.

David
--
---
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/.
g***@gmail.com
2015-04-22 11:13:19 UTC
Permalink
On Wednesday, April 22, 2015 at 7:00:50 PM UTC+12, David Rodríguez Ibeas
Post by David Rodríguez Ibeas
Post by g***@gmail.com
I have seen a lot of code like that, which is technically broken then.
I've written code like that myself regarding string.
I think perhaps the standard should change to make such code work as
expected for things like string.
I have seen a lot of people expecting wrong programs to behave correctly,
that does not mean that the language has to adjust to match the
expectations of those people, rather that those people need to learn what
can and cannot be done within the bounds of the language.
People expects arrays and pointers to be the same, believe that
dereferencing null is fine as long as you immediately take the address of
the lvalue or a member of it, you can do network-to-host of a double in
place through reinterpret_cast<int64_t*> and ntohll... should all those be
adjusted to the expectations of users?
Expecting that the resulting string is empty might not be a big change,
but the moved-from object would have to undertake two modifications that
are not needed today: reset the size to 0, set the first character in the
buffer to 0. Neither of those operations help the cause of moving from a
real rvalue and feel a bit like setting a pointer to nullptr after deleting
the object in the destructor, not really that useful. If you really want
the string to be cleared you have the option of paying for the operation
yourself, without forcing all other uses onto that cost.
David
You are exactly correct about what I think the "problem" is and what I
think the possible "solution" might be.
Most implementations (it appears) already conform to the design that I find
least surprising and that's probably exactly why.
i.e. moving from a string leaves it empty.
But if we are saying code shouldn't rely on that, then I think we need
compilers to diagnose our wrong assumptions here and/or have some major
implementations disagree, so we are less likely to rely on non guaranteed
behaviour.
Or we need to make the practice standard so they can rely on it.
If we do nothing, I think eventually there will be a large body of
code that needs support and we'll standardize it anyway.
I don't think we should sleep walk in that which I think we will do if
stick to the status quo.
--
---
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/.
Nevin Liber
2015-04-22 14:59:44 UTC
Permalink
Post by g***@gmail.com
You are exactly correct about what I think the "problem" is and what I
think the possible "solution" might be.
Most implementations (it appears) already conform to the design that I
find least surprising and that's probably exactly why.
Do those implementations even conform to C++11? For instance, the string
implementation in gcc won't until gcc5.
Post by g***@gmail.com
But if we are saying code shouldn't rely on that, then I think we need
compilers to diagnose our wrong assumptions here and/or have some major
implementations disagree, so we are less likely to rely on non guaranteed
behaviour.
Talk to your implementation vendor.
Post by g***@gmail.com
If we do nothing, I think eventually there will be a large body of
code that needs support and we'll standardize it anyway.
The intent is that the moved-from string is either destroyed or replaced
using assignment. Anything else is a micro optimization that most people
really shouldn't be doing. Just sprinkling std::move all over the place
makes code harder to understand, and in some cases, is even a pessimization
(such as when interfering with RVO).

And what about calls which only conditionally do a move, such as
set<T>::insert(T&&)?
--
Nevin ":-)" Liber <mailto:***@eviloverlord.com> (847) 691-1404
--
---
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/.
g***@gmail.com
2015-04-22 22:29:19 UTC
Permalink
Post by Nevin Liber
Post by g***@gmail.com
You are exactly correct about what I think the "problem" is and what I
think the possible "solution" might be.
Most implementations (it appears) already conform to the design that I
find least surprising and that's probably exactly why.
Do those implementations even conform to C++11? For instance, the string
implementation in gcc won't until gcc5.
Post by g***@gmail.com
But if we are saying code shouldn't rely on that, then I think we need
compilers to diagnose our wrong assumptions here and/or have some major
implementations disagree, so we are less likely to rely on non guaranteed
behaviour.
Talk to your implementation vendor.
Post by g***@gmail.com
If we do nothing, I think eventually there will be a large body of
code that needs support and we'll standardize it anyway.
The intent is that the moved-from string is either destroyed or replaced
using assignment. Anything else is a micro optimization that most people
really shouldn't be doing. Just sprinkling std::move all over the place
makes code harder to understand, and in some cases, is even a pessimization
(such as when interfering with RVO).
And what about calls which only conditionally do a move, such as
set<T>::insert(T&&)?
--
691-1404
Hi Nevin

I think my reply to Howard might respond to some of your thoughts. My
concern is that move opens up a lot of things whereby people are doing
things that they really shouldn't be doing. I'm just trying to find ways
where we turn more of those things into completely legal things or
definitely errant things so that the compiler knows that.

Your conditional move function is interesting because even if
certain functions are "safe" to call in a sense after being moved from,
what happens is still dependent potentially on if an earlier move did or
didn't actually happen and not being aware of that state is where one can
easily introduce bugs.

In answer to your question about C++11 conformance. I don't know but I
suspect the conformance level won't change the result of my earlier example
or peoples opinions on the cross platform reliability of it's output.
People are already saying it's not reliable and it will worry me if we
can't depend on it or can't diagnose it more.
--
---
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-04-23 03:59:23 UTC
Permalink
And what about calls which only conditionally do a move, such as set<T>::insert(T&&)?
I don’t think the standard has a notion of conditional moving. You always have to assume that the object has been moved-from.

It would be nice to see standardization of conditional moves, but the optimization is patchily applied in practice. I made the mistake once of trying to move an object into a map, moving it somewhere else upon key collision. It went into the bit-bucket instead.

Requiring that moved-from objects be reset to default-initialized state would entail some pessimization. Until the implementations and the standard can converge on some best behavior, unspecified behavior looks like the right choice.
--
---
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/.
Howard Hinnant
2015-04-22 14:45:14 UTC
Permalink
Are you saying that this output is undefined, i.e. that on another machine the output might be something different?
Yes. Not quite undefined, but in a valid but unspecified state.
valid but unspecified state
an object state that is not specified except that the object’s invariants are met and operations on the object behave as specified for its type
[ Example: If an object x of type std::vector<int> is in a valid but unspecified state, x.empty() can be called unconditionally, and x.front() can be called only if x.empty() returns false. —end example ]
--
I have seen a lot of code like that, which is technically broken then. I've written code like that myself regarding string.
I think perhaps the standard should change to make such code work as expected for things like string.
e.g.: guarantee this assert will never fail.
std::string s1 = "hello";
std::string s2 = std::move(s)
assert(s1.empty());
What says the people?
Assuming you meant:

std::string s2 = std::move(s1);

———

Questions:

1. Is this guarantee intended only for std::string, and not other instantiations of std::basic_string?

2. Is this guarantee intended only for the move constructor, or is it also intended for the move assignment operator? Or what about other operations within the std::lib that take a std::string&& (or std::basic_string<…>&&) such as vector<string>::insert(const_iterator, string&&)?

Howard
--
---
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/.
g***@gmail.com
2015-04-22 22:09:34 UTC
Permalink
Post by g***@gmail.com
Post by g***@gmail.com
On Wednesday, April 22, 2015 at 1:52:41 PM UTC+12, Nevin ":-)" Liber
Are you saying that this output is undefined, i.e. that on another
machine the output might be something different?
Post by g***@gmail.com
Yes. Not quite undefined, but in a valid but unspecified state.
valid but unspecified state
an object state that is not specified except that the object’s
invariants are met and operations on the object behave as specified for its
type
Post by g***@gmail.com
[ Example: If an object x of type std::vector<int> is in a valid but
unspecified state, x.empty() can be called unconditionally, and x.front()
can be called only if x.empty() returns false. —end example ]
Post by g***@gmail.com
--
I have seen a lot of code like that, which is technically broken then.
I've written code like that myself regarding string.
Post by g***@gmail.com
I think perhaps the standard should change to make such code work as
expected for things like string.
Post by g***@gmail.com
e.g.: guarantee this assert will never fail.
std::string s1 = "hello";
std::string s2 = std::move(s)
assert(s1.empty());
What says the people?
std::string s2 = std::move(s1);
———
1. Is this guarantee intended only for std::string, and not other
instantiations of std::basic_string?
2. Is this guarantee intended only for the move constructor, or is it also
intended for the move assignment operator? Or what about other operations
within the std::lib that take a std::string&& (or std::basic_string<
>&&)
such as vector<string>::insert(const_iterator, string&&)?
Howard
Hi Howard

It's also not practical for me to answer your questions explicitly because
I suspect the answer requires a detailed analysis of not just string but
other types like vector etc. too.

But I am not a strong advocate that a guarantee should be given at all.

The example was to illustrate my concern that if we carry on as we are
today, we may have to guarantee what the output of my trivial example is,
because so much code like it will exist that depends on what we are already
doing today; and that we may already have reached that point.

To my mind, the safest, fastest and easiest thing to teach is the current
situation: that a moved from object should not be used at all. But the
problem with that rule that it's not natural. That doesn't make the rule
wrong because it is simple to teach, but it's still not natural because to
my mind it's not what we first assume. It's just way too easy to move from
a string and then expect the moved from string to be empty and re-useable
and that belief is strengthened by the fact that in all the compilers and
libraries I tried my example on that, the string was empty in practice.

I built my example (sorry for the typo by the way) with libc++ and clang
but also with g++ 4.9 with the mingw libraries and with msvc. In all cases
the string was left empty. I haven't looked at the code of these libraries
to see if they contain instructions that encourage reusability, but I
suspect some do. But I also suspect that in some cases the instructions are
just a by-product of what a class needs to do for it's own sanity on
destruction.

The worry though is that despite these results, we are still saying the
code in my example is unreliable. Yet no compiler I tried gave me any
warning about using a moved from type and because of the other things I've
said it might not even be trivial or even possible to do so.

There may be inherent performance tensions between reusing an object and
not reusing one too in many circumstances.

So what I'm saying is this all feels like it's something that we should be
concerned about and can we improve the situation?

I think what we are doing today through insufficient compiler
warnings or possibly assisting libraries is building up a stock of errant
code that if we don't do something about it soon, it may mean having to
give guarantees we don't like for various reasons. I am sure there must be
some precedent in the past where that has had to be done too.

I would be nice if compilers could put more effort into saying "hey, this
type may have been moved from, so don't use it further" but is that
viable. But without further attention focused in this space, be it by
books, compiler warnings or library changes or attributes, I worry that
moved from objects might be a new major source of bugs in C++ code going
forward.

Don't get me wrong, I think move is awesome, but can we do more to manage
it's impact better?
Maybe we designate a [[safe_after_move]] class or function attribute or
something and then have the compiler attempt to warn on use of any other
method after a move has been seen. It might not be bullet proof but maybe
it might help.
--
---
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/.
Nevin Liber
2015-04-22 22:28:07 UTC
Permalink
Post by g***@gmail.com
Don't get me wrong, I think move is awesome, but can we do more to manage
it's impact better?
Maybe we designate a [[safe_after_move]] class or function attribute or
something and then have the compiler attempt to warn on use of any other
method after a move has been seen. It might not be bullet proof but maybe
it might help.
The attribute doesn't help. Someone has to spend the effort to make a
thoroughly checking standard library (such as Microsoft did with things
like checked iterators). That's a vendor issue, not a standards issue.
--
Nevin ":-)" Liber <mailto:***@eviloverlord.com> (847) 691-1404
--
---
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/.
Howard Hinnant
2015-04-23 00:01:49 UTC
Permalink
———
Post by Howard Hinnant
1. Is this guarantee intended only for std::string, and not other instantiations of std::basic_string?
2. Is this guarantee intended only for the move constructor, or is it also intended for the move assignment operator? Or what about other operations within the std::lib that take a std::string&& (or std::basic_string<…>&&) such as vector<string>::insert(const_iterator, string&&)?
It's also not practical for me to answer your questions explicitly because I suspect the answer requires a detailed analysis of not just string but other types like vector etc. too.
It is difficult to analyze a proposal without knowing exactly what is being proposed.
Maybe we designate a [[safe_after_move]] class or function attribute or something and then have the compiler attempt to warn on use of any other method after a move has been seen. It might not be bullet proof but maybe it might help.
Perhaps it would be instructive to recast the question in terms we’re familiar with from C++98. What should this program output?

#include <iostream>
#include <algorithm>
#include <string>

int
main()
{
std::string array[] = {"a", "b", "c"};
const std::size_t sz = sizeof(array)/sizeof(array[0]);
std::remove(array, array+sz, "a");
std::cout << array[sz-1] << '\n';
}

What operations would it be safe to do with the value array[sz-1] after std::remove is called? Is the answer to that question different between C++98, C++03, C++11 and C++14?

Is their any practical difference between array[sz-1] and a C++11 moved-from std::string? Is the issue with std::remove one that has needed (and continues to need) to be solved (since C++98)?

If we were to put the call to std::remove under a try/catch, in the catch clause what operations would be safe to perform on the elements of array?

I guess what I’m getting around to is: Valid but unspecified states are not a new thing with C++11. We have lived with them everywhere we have basic exception safety, and in a few more places such as std::remove, std::remove_if and std::unique. If moved-from std::strings need fixing, don’t we also need to address these other areas as well for consistency? Or is move that special?

Howard
--
---
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/.
g***@gmail.com
2015-04-23 00:48:30 UTC
Permalink
Post by Howard Hinnant
Post by g***@gmail.com
———
Post by Howard Hinnant
1. Is this guarantee intended only for std::string, and not other
instantiations of std::basic_string?
Post by g***@gmail.com
Post by Howard Hinnant
2. Is this guarantee intended only for the move constructor, or is it
also intended for the move assignment operator? Or what about other
operations within the std::lib that take a std::string&& (or
std::basic_string<
>&&) such as vector<string>::insert(const_iterator,
string&&)?
Post by g***@gmail.com
It's also not practical for me to answer your questions explicitly
because I suspect the answer requires a detailed analysis of not just
string but other types like vector etc. too.
It is difficult to analyze a proposal without knowing exactly what is being proposed.
It is. But I didn't propose anything because of what your later
question acknowledges. You say "If moved-from std::strings need fixing,
don’t we also need to address these other areas as well for consistency?".

And the answer to that is "yes, it might.", but to answer in any more
detail is to go list all those other types and get everyone to agree with
that and I feel that's to big of a task for me. It's also not something I
was strongly advocating. I was looking as much to the other direction in my
post as that one.
Post by Howard Hinnant
Post by g***@gmail.com
Maybe we designate a [[safe_after_move]] class or function attribute or
something and then have the compiler attempt to warn on use of any other
method after a move has been seen. It might not be bullet proof but maybe
it might help.
Perhaps it would be instructive to recast the question in terms we’re
familiar with from C++98. What should this program output?
#include <iostream>
#include <algorithm>
#include <string>
int
main()
{
std::string array[] = {"a", "b", "c"};
const std::size_t sz = sizeof(array)/sizeof(array[0]);
std::remove(array, array+sz, "a");
std::cout << array[sz-1] << '\n';
}
What operations would it be safe to do with the value array[sz-1] after
std::remove is called? Is the answer to that question different between
C++98, C++03, C++11 and C++14?
Is their any practical difference between array[sz-1] and a C++11
moved-from std::string? Is the issue with std::remove one that has needed
(and continues to need) to be solved (since C++98)?
If we were to put the call to std::remove under a try/catch, in the catch
clause what operations would be safe to perform on the elements of array?
I guess what I’m getting around to is: Valid but unspecified states are
not a new thing with C++11. We have lived with them everywhere we have
basic exception safety, and in a few more places such as std::remove,
std::remove_if and std::unique. If moved-from std::strings need fixing,
don’t we also need to address these other areas as well for consistency?
Or is move that special?
Howard
I think that move is special in that firstly, it is a core operation that
pretty much applies to most types. And secondly, once applied, it
obliterates the object pretty profoundly.

None of this is to say that the design of move is wrong.

But what bothers me is that even for my simple example, the result is that
apparently (listening to the replies already in) can't guarantee even that
code will reliably output the same thing on different platforms AND that no
compiler I have seen currently diagnoses the issue. It may not even be
possible. That's what worries me.

Your earlier question acknowledges the kinds of things that need to be
answered which ever road you go down and the complexity of answering that
which is why I haven't tried.

All I'm doing is just laying out what I see as the current state and saying
they seem less than ideal and asking what can we do about it. I want the
compiler to be able to spot errant use after move as much as we spot errant
use after delete and/or to make sensible use after move less platform
specific. There may be no perfect here. But better might be obtainable. I
like move I just want to make using it less of a source of bugs and am
interested in ways of achieving that if it's possible.
--
---
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/.
Howard Hinnant
2015-04-23 01:09:36 UTC
Permalink
And secondly, once applied, it obliterates the object pretty profoundly.
This is a notion I’d prefer to reword. In the early days of move this was the prevailing thought. I.e. you can’t do anything with a moved-from object but destruct it! But that really isn’t right. After all, if that were true, std::swap wouldn’t swap values, it would just destruct them. :-) That is, std::swap brings moved-from values “back from the dead”.

Indeed, during a sort, everything gets moved, and every moved-from value is given a new one.
None of this is to say that the design of move is wrong.
No worries, I’m not interpreting your thoughts that way.
But what bothers me is that even for my simple example, the result is that apparently (listening to the replies already in) can't guarantee even that code will reliably output the same thing on different platforms AND that no compiler I have seen currently diagnoses the issue. It may not even be possible. That's what worries me.
Your worries are not without justification.
Your earlier question acknowledges the kinds of things that need to be answered which ever road you go down and the complexity of answering that which is why I haven't tried.
All I'm doing is just laying out what I see as the current state and saying they seem less than ideal and asking what can we do about it. I want the compiler to be able to spot errant use after move as much as we spot errant use after delete and/or to make sensible use after move less platform specific. There may be no perfect here. But better might be obtainable. I like move I just want to make using it less of a source of bugs and am interested in ways of achieving that if it's possible.
I appreciate your efforts in this area. I am hearing from multiple sources that std::move is overused and that moved-from values are not being correctly handled. Right now I do not know what the solution is, nor if a solution is possible, nor even if this is something that needs to be solved beyond education on std::move. I remain interested in the domain, and will contribute whatever background knowledge I can towards everyone's efforts to better C++.

Howard
--
---
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/.
g***@gmail.com
2015-04-23 06:27:15 UTC
Permalink
And secondly, once applied, it obliterates the object pretty profoundly.
This is a notion I’d prefer to reword. In the early days of move this was
the prevailing thought. I.e. you can’t do anything with a moved-from
object but destruct it! But that really isn’t right. After all, if that
were true, std::swap wouldn’t swap values, it would just destruct them. :-)
That is, std::swap brings moved-from values “back from the dead”.
This aspect is definitely the interesting thing. My own experience was
the opposite way around: first I believed that "of course you can reuse an
object after moving from it, within reason*", as that just seemed sensible.

But then my colleagues would say "oh, no you must never use an object *at
all* after it's been moved from" and I would think, hmmm, ok, that's simple
to remember, but seems a bit draconian and unnatural.

But to my mind it starts to get really worrisome if you have a situation
where people can't even agree or be sure if a piece of code as simple as my
example should work AND no compiler diagnose the problem which is where I
think we are today.

And whatever view you take, if things aren't clear people
and people start rewriting code to reflect their own opinion, that isn't
good either.

The worst part for me is that I have found that my own view oscillates
between the above two views for various reasons.

My view point 1 is that an object that has been moved from should generally
attempt to put itself into a state that it has the same usability
guarantees as if has if it had been default constructed. This model
seems natural and it would fix a certain source of bugs and it would make
the simple string example work consistently which I find appealing.

But my contrary view says that offering any such a guarantee might be
sub-optimal because my guess is that most objects are never used again
after they have been moved from so not having to reset to a definite
state might improve performance.

And the most bothersome kind of code is code that conditionally moves
(either by error or by design) because that seems to invite a dangerous
source of bugs - i.e. where you can end up appending to a string you
thought was empty but it turns out that it isn't because a move didn't
happen for some reason.

Adopting a clear "don't re-use after move" rule would seem to help in the
latter cases but it makes my simple code example unreliable and I hate that.

But whatever we recommend, I think it would be a significant win if the
compiler was able to spot when I'm doing it wrong.

Off the top of my head, maybe the default should be that objects
are considered not reusable and any function that can move should be
considered to have moved by default and the compiler should be mandated to
issue a warning if you attempt to reuse an object without being explicit.

For example, imagine if this were the default:

void take_me_lord(my_object&& ashes_to_ashes);
my_object goodbye_cruel_world("so long");
take_me_lord( goodbye_cruel_world );
goodbye_cruel_world = "resurrection"; // COMPILER ERROR:

But to get around it you had to write:

void take_me_lord(my_object&& ashes_to_ashes);
my_object goodbye_cruel_world("so long");
take_me_lord( goodbye_cruel_world );
reusing goodbye_cruel_world; // New construct.
goodbye_cruel_world = "resurrection"; // It works, it's a miracle.

The re-using statement could be any number of things:
1. just a device to prevent a compiler warning.
2. or it could do something like call a re-constructor.

A re-constructor could put an existing object into a state where it can be
destructed or reused as if it had just been constructed.
The default implementation could be:
template<T> void reconstruct( T& existing_obj )
{
~existing_obj();
new(existing_obj)();
}
but the user could provide a more efficient implementation where it made
sense.
e.g. a string could just sets it's length to 0 and then that code could be
omitted from it's move functions.

Anyway, I'm brain storming off the top of my head. I haven't thought about
any of that too deeply as to if it makes sense or is actually viable or is
just plane worse than what we have already.

But it would seem to enable the compiler to immediately say that anything
that appears after an std::move(x) is an error unless it is followed by a
re-using statement to explicitly ignore that warning and maybe do something
more and it would seem to allow some move operations to be simpler so that
they don't incur a cost unless a re-use actually happens and it makes
re-use intent clear and explicit.

Just some thoughts to help debate.
Indeed, during a sort, everything gets moved, and every moved-from value
is given a new one.
None of this is to say that the design of move is wrong.
No worries, I’m not interpreting your thoughts that way.
But what bothers me is that even for my simple example, the result is
that apparently (listening to the replies already in) can't guarantee even
that code will reliably output the same thing on different platforms AND
that no compiler I have seen currently diagnoses the issue. It may not even
be possible. That's what worries me.
Your worries are not without justification.
Your earlier question acknowledges the kinds of things that need to be
answered which ever road you go down and the complexity of answering that
which is why I haven't tried.
All I'm doing is just laying out what I see as the current state and
saying they seem less than ideal and asking what can we do about it. I want
the compiler to be able to spot errant use after move as much as we spot
errant use after delete and/or to make sensible use after move less
platform specific. There may be no perfect here. But better might be
obtainable. I like move I just want to make using it less of a source of
bugs and am interested in ways of achieving that if it's possible.
I appreciate your efforts in this area. I am hearing from multiple
sources that std::move is overused and that moved-from values are not being
correctly handled. Right now I do not know what the solution is, nor if a
solution is possible, nor even if this is something that needs to be solved
beyond education on std::move. I remain interested in the domain, and will
contribute whatever background knowledge I can towards everyone's efforts
to better C++.
Howard
Thanks Howard. Move is a great feature. It'll be cool if it could be made
even better, but I appreciate it might not be trivial to uncover exactly
how to do that.
--
---
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/.
Greg Marr
2015-04-23 20:33:35 UTC
Permalink
Post by g***@gmail.com
My view point 1 is that an object that has been moved from should
generally attempt to put itself into a state that it has the same usability
guarantees as if has if it had been default constructed. This model
seems natural and it would fix a certain source of bugs and it would make
the simple string example work consistently which I find appealing.
That sounds very similar to a valid but unspecified state. The difference
being just that you don't know what's in the object, and by using
std::move() you've explicitly said that you don't care, because you're
either going to destroy it or sets its state to something known.

But my contrary view says that offering any such a guarantee might be
Post by g***@gmail.com
sub-optimal because my guess is that most objects are never used again
after they have been moved from so not having to reset to a definite
state might improve performance.
And the most bothersome kind of code is code that conditionally moves
(either by error or by design) because that seems to invite a dangerous
source of bugs - i.e. where you can end up appending to a string you
thought was empty but it turns out that it isn't because a move didn't
happen for some reason.
Adopting a clear "don't re-use after move" rule would seem to help in the
latter cases but it makes my simple code example unreliable and I hate that.
It's not "don't re-use after move", it's "don't re-use after move until
you've put it into a known state".
Post by g***@gmail.com
void take_me_lord(my_object&& ashes_to_ashes);
my_object goodbye_cruel_world("so long");
take_me_lord( goodbye_cruel_world );
That being a compiler error would be very bad. That is perfectly valid
code today for well-behaved classes such as std::string, assuming the
addition of the missing std::move() in the take_me_lord() call, since it
won't compile as-is because it's not an rvalue. I've heard several people
say, and I agree, that if you read std::move() as std::rvalue_cast(), then
it makes more sense that you're not actually moving when you do
std::move(), you're telling the called function that it's okay to move from
this object.

A re-constructor could put an existing object into a state where it can be
Post by g***@gmail.com
destructed or reused as if it had just been constructed.
You don't need a re-constructor for this, as this is mostly what already
happens. You can reuse it as long as you use a function that sets the
entire state, and not one that simply modifies the existing state. So you
can use std::string::operator=(), but std::string::operator+=() will give
you unspecified results.
--
---
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/.
g***@gmail.com
2015-04-23 22:14:38 UTC
Permalink
Post by Greg Marr
Post by g***@gmail.com
My view point 1 is that an object that has been moved from should
generally attempt to put itself into a state that it has the same usability
guarantees as if has if it had been default constructed. This model
seems natural and it would fix a certain source of bugs and it would make
the simple string example work consistently which I find appealing.
That sounds very similar to a valid but unspecified state. The difference
being just that you don't know what's in the object, and by using
std::move() you've explicitly said that you don't care, because you're
either going to destroy it or sets its state to something known.
The difference is consistency - programmers mental model. Currently, after
a move (such as on a string) a types "valid but unspecified state is" so
unspecified that even my simple string example from earlier can't be
guaranteed to work i.e. even length cannot be guaranteed to be 0.
But because it feels natural that a string should be empty after a
move and because it usually does turn out to be in practice programmers are
making the mistake of relying on that. If you changed the implementation of
string to artificially change length to a random number after move, I think
a lot of code would break / you'd find a lot of bugs.

If types could be generally said to reset to their default initialized
state this would fix some of those bugs and make a clearer mental model as
to what methods are valid after move, because if you are familiar with a
type to use it, you are familiar with it's default initialized state to
know what you can do with it.

The ideas was about attempting to make more methods valid after use a move
and an off the top of my head idea aimed at making the type of code I
already see "out there" like the string example that is currently
technically wrong, valid again.

It's not an idea I am strongly attached to, I am actually trying to branch
out to explore to opposite directions either or neither of which might
improve on the status quo.
So this idea is about trying to find new ideas for discussion about the
issue.
And the answer to your next question relates to the exploration of the
other direction.
Post by Greg Marr
But my contrary view says that offering any such a guarantee might be
Post by g***@gmail.com
sub-optimal because my guess is that most objects are never used again
after they have been moved from so not having to reset to a definite
state might improve performance.
And the most bothersome kind of code is code that conditionally moves
(either by error or by design) because that seems to invite a dangerous
source of bugs - i.e. where you can end up appending to a string you
thought was empty but it turns out that it isn't because a move didn't
happen for some reason.
Adopting a clear "don't re-use after move" rule would seem to help in the
latter cases but it makes my simple code example unreliable and I hate that.
It's not "don't re-use after move", it's "don't re-use after move until
you've put it into a known state".
Yes I know. But the "problem" here is that the compiler has no idea here
*which* function(s) will put the object into a state where it is more
reusable again and consequently if you start using ones you shouldn't, it
can't warn you.

I want to find a way if possible to allow the compiler to say "hey you
probably have just moved from this type and now you are calling methods
that probably aren't legal yet, call one of these methods first to make it
legal".

So for example, if we could decorate functions with an attribute that says
"call me first after move" the idea is the compiler assumes that after a
move, an object is likely fragile, so if you call just any function, it's
likely to not be correct and it'll warn you, but it can see you have called
one of these other "resurrection" type attributed functions first, then it
could supress that warning.
Post by Greg Marr
Post by g***@gmail.com
void take_me_lord(my_object&& ashes_to_ashes);
my_object goodbye_cruel_world("so long");
take_me_lord( goodbye_cruel_world );
That being a compiler error would be very bad. That is perfectly valid
code today for well-behaved classes such as std::string, assuming the
addition of the missing std::move() in the take_me_lord() call, since it
won't compile as-is because it's not an rvalue. I've heard several people
say, and I agree, that if you read std::move() as std::rvalue_cast(), then
it makes more sense that you're not actually moving when you do
std::move(), you're telling the called function that it's okay to move from
this object.
A re-constructor could put an existing object into a state where it can be
Post by g***@gmail.com
destructed or reused as if it had just been constructed.
You don't need a re-constructor for this, as this is mostly what already
happens. You can reuse it as long as you use a function that sets the
entire state, and not one that simply modifies the existing state. So you
can use std::string::operator=(), but std::string::operator+=() will give
you unspecified results.
So you can see the goals are to try to make more currently unreliable code
like my string example reliable if possible and to make the compiler better
able to stop you making this kinds of mistakes in the first place.

My two ideas were off the top of my head thoughts to start discussion on
that. But they were looking in opposite directions for the answer. One is
to try to make types more consistent after move such that a broader range
of operations would just be valid by default using a mental model the
programmer can imagine easily for their type - they know it's default
constructed state.

And the other idea was to let the compiler in on what functions need to be
called to resurrect a class and not just assume any function was ok. In
essence to try to provide the compiler with knowledge of the protocol we
expect to follow for types where possible so it can inform us when we
violate that.

If we don't improve on the situation as it is today we have bugs like my
string example going undetected and the 'just don't use it again after a
move' starts to become the safest thing do to avoid bugs even though
that's technically that is overkill.

Neither idea once here may be appropriate I was just exploring off the top
of my head. But I think I have articulated the goal I am aiming at quite
clearly - let's try to see if we can do more to reduce 'incorrect use after
move' errors and turn more of them into compile time errors.
--
---
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/.
Nevin Liber
2015-04-24 02:29:16 UTC
Permalink
Post by g***@gmail.com
Post by Greg Marr
Post by g***@gmail.com
My view point 1 is that an object that has been moved from should
generally attempt to put itself into a state that it has the same usability
guarantees as if has if it had been default constructed. This model
seems natural and it would fix a certain source of bugs and it would make
the simple string example work consistently which I find appealing.
That sounds very similar to a valid but unspecified state. The
difference being just that you don't know what's in the object, and by
using std::move() you've explicitly said that you don't care, because
you're either going to destroy it or sets its state to something known.
The difference is consistency - programmers mental model.
Not fitting your mental model is not a bug in the standard.

I don't know how you solve the problem of people not reading documentation,
because the number of different mental models that people can make up that
don't fit the specification is unbounded.
Post by g***@gmail.com
Currently, after a move (such as on a string) a types "valid but
unspecified state is" so unspecified that even my simple string example
from earlier can't be guaranteed to work i.e. even length cannot
be guaranteed to be 0.
Because *unspecified* means *not specified*. I really don't know how it
can be made any clearer. Would you prefer we pick a different word out of
a thesaurus? You seem to think that *unspecified* should mean *specified*.

But because it feels natural that a string should be empty after a
Post by g***@gmail.com
move and because it usually does turn out to be in practice programmers are
making the mistake of relying on that. If you changed the implementation of
string to artificially change length to a random number after move, I think
a lot of code would break / you'd find a lot of bugs.
Many people have an oversimplified mental model of the underlying machine
too, with no caches and atomic access to each and every primitive type.
They sprinkle "volatile" all over their variables because they think it
magically solves threading and concurrency problems. What should we do
about that, as the bugs it causes are far more insidious?

If types could be generally said to reset to their default initialized
Post by g***@gmail.com
state this would fix some of those bugs
But not all of them, because the mental model would still be wrong. It
would just make correct code slower.

Your mental model is inconsistent with what an arbitrary developer can do
with his/her move constructor for a user defined type.

Plus, there are reasons to use r-value references besides intent to move.
It indicates an unnamed temporary, and can be useful in things like
expression templates, where the classes should not be holding on to
references/pointers past the full expression it is a part of.
Post by g***@gmail.com
If we don't improve on the situation as it is today we have bugs like my
string example going undetected and the 'just don't use it again after a
move' starts to become the safest thing do to avoid bugs even though
that's technically that is overkill.
What's wrong with that, other than it being overly pessimistic?

Over time, you can teach them a better mental model:


- Assignment will put the object back into a specific state and makes it
once again effectively useable.
- For standard library containers, calling clear() will also the object
back into a specific state and makes it once again effectively useable.


While that may not always be 100% optimal, IMNSHO anything more
fine-grained needs a measured performance bottleneck before pursuing.


Or you can write your own library that matches your mental model.
--
Nevin ":-)" Liber <mailto:***@eviloverlord.com> (847) 691-1404
--
---
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/.
g***@gmail.com
2015-04-24 11:59:36 UTC
Permalink
Post by Nevin Liber
Post by g***@gmail.com
Post by Greg Marr
Post by g***@gmail.com
My view point 1 is that an object that has been moved from should
generally attempt to put itself into a state that it has the same usability
guarantees as if has if it had been default constructed. This model
seems natural and it would fix a certain source of bugs and it would make
the simple string example work consistently which I find appealing.
That sounds very similar to a valid but unspecified state. The
difference being just that you don't know what's in the object, and by
using std::move() you've explicitly said that you don't care, because
you're either going to destroy it or sets its state to something known.
The difference is consistency - programmers mental model.
Not fitting your mental model is not a bug in the standard.
Well that is arguable depending on how you want to determine what a bug is
A moved from string that has a length that is unspecified or that has to be
assigned and empty string to be usable is odd at first inspection.
One could argue that it's an unintuitive design that means it may as well
be a bug in the standard in the same way that auto_ptr was.
If it leads to a lot of bugs maybe it needs to be replaced with something
that doesn't do that so easily in the same way unique_ptr was added.
Post by Nevin Liber
I don't know how you solve the problem of people not reading documentation,
because the number of different mental models that people can make up
that don't fit the specification is unbounded.
Nor do I. But we could say the same about use after free, but it didn't
stop people from building better schemes to detect that. That's all I'm
suggesting look into. Let's not be hostile to that.
Post by Nevin Liber
Currently, after a move (such as on a string) a types "valid but
Post by g***@gmail.com
unspecified state is" so unspecified that even my simple string example
from earlier can't be guaranteed to work i.e. even length cannot
be guaranteed to be 0.
Because *unspecified* means *not specified*. I really don't know how it
can be made any clearer. Would you prefer we pick a different word out of
a thesaurus? You seem to think that *unspecified* should mean *specified*
.
I know what it means. I've been clear about what I think the issue is even
a solution for it might not be easy or possible. But you getting hung up on
words doesn't help.

But because it feels natural that a string should be empty after a
Post by Nevin Liber
Post by g***@gmail.com
move and because it usually does turn out to be in practice programmers are
making the mistake of relying on that. If you changed the implementation of
string to artificially change length to a random number after move, I think
a lot of code would break / you'd find a lot of bugs.
Many people have an oversimplified mental model of the underlying machine
too, with no caches and atomic access to each and every primitive type.
They sprinkle "volatile" all over their variables because they think it
magically solves threading and concurrency problems. What should we do
about that, as the bugs it causes are far more insidious?
If types could be generally said to reset to their default initialized
Post by g***@gmail.com
state this would fix some of those bugs
But not all of them, because the mental model would still be wrong. It
would just make correct code slower.
Your mental model is inconsistent with what an arbitrary developer can do
with his/her move constructor for a user defined type.
Plus, there are reasons to use r-value references besides intent to move.
It indicates an unnamed temporary, and can be useful in things like
expression templates, where the classes should not be holding on to
references/pointers past the full expression it is a part of.
Post by g***@gmail.com
If we don't improve on the situation as it is today we have bugs like my
string example going undetected and the 'just don't use it again after a
move' starts to become the safest thing do to avoid bugs even though
that's technically that is overkill.
What's wrong with that, other than it being overly pessimistic?
- Assignment will put the object back into a specific state and makes
it once again effectively useable.
- For standard library containers, calling clear() will also the
object back into a specific state and makes it once again effectively
useable.
While that may not always be 100% optimal, IMNSHO anything more
fine-grained needs a measured performance bottleneck before pursuing.
The issue isn't about performance, it's about reducing bugs related to
using move.
The only issue related to performance is making any solution doesn't
degrade performance as to make it unviable.
Post by Nevin Liber
Or you can write your own library that matches your mental model.
Again this misses the point. Something that just helps my own mental model
but doesn't gel with everyone else's is too niche to bother with.
Post by Nevin Liber
--
691-1404
--
---
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 Rodríguez Ibeas
2015-04-24 14:04:56 UTC
Permalink
Post by g***@gmail.com
A moved from string that has a length that is unspecified or that has to
be assigned and empty string to be usable is odd at first inspection.
An 'int' that is uninitialized and has to be assigned to in order to be
usable is odd at first impression.


The issue isn't about performance, it's about reducing bugs related to
Post by g***@gmail.com
using move.
But it is. There is a balance between the guarantees that you want to
provide and the cost that providing those guarantees impose on the
program. The main use of functions taking rvalue-references is to take
advantage of an rvalue that won't be used after this operation, yes, you
can bind an rvalue-reference to an lvalue and steal from the lvalue, but
that is not the primary use case. Adding code that resets the string to a
well known state would make every use of the move operations incur a cost,
small as it may be, that is not needed.

Consider a large vector<string>, say 10M elements, and erasing the first
element. Do you want to trigger 10M 'clear()' followed by move assignments?
I'd rather avoid the 'clear' as the move assignments will guarantee the
final value without going through some intermediate guaranteed to be empty
state.

David
--
---
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/.
g***@gmail.com
2015-04-24 23:17:59 UTC
Permalink
On Saturday, April 25, 2015 at 2:04:59 AM UTC+12, David Rodríguez Ibeas
Post by David Rodríguez Ibeas
Post by g***@gmail.com
A moved from string that has a length that is unspecified or that has to
be assigned and empty string to be usable is odd at first inspection.
An 'int' that is uninitialized and has to be assigned to in order to be
usable is odd at first impression.
The issue isn't about performance, it's about reducing bugs related to
Post by g***@gmail.com
using move.
But it is. There is a balance between the guarantees that you want to
provide and the cost that providing those guarantees impose on the program.
If you take the full sentence of what you have cut short I acknowledge that
there is a possible tension. But never the less performance isn't the focus
of the issue - i.e. I am not trying to make programs faster. It is a goal
not to make them slower though such that it makes the true goal of reducing
move bugs unattainable. Your comments only echo what I am saying but in a
way that makes it sound like I'm saying something else.
Post by David Rodríguez Ibeas
The main use of functions taking rvalue-references is to take advantage
of an rvalue that won't be used after this operation, yes, you can bind an
rvalue-reference to an lvalue and steal from the lvalue, but that is not
the primary use case. Adding code that resets the string to a well known
state would make every use of the move operations incur a cost, small as it
may be, that is not needed.
Consider a large vector<string>, say 10M elements, and erasing the first
element. Do you want to trigger 10M 'clear()' followed by move assignments?
I'd rather avoid the 'clear' as the move assignments will guarantee the
final value without going through some intermediate guaranteed to be empty
state.
No I don't want to trigger that but there maybe a trade off of cost that is
acceptable. I doubt it, but that's part of the debate and I was
drawing attention to that.
Post by David Rodríguez Ibeas
David
--
---
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/.
Thiago Macieira
2015-04-24 16:04:36 UTC
Permalink
Post by g***@gmail.com
The issue isn't about performance, it's about reducing bugs related to
using move.
Then it's a QoI issue: please file a suggestion for improvement to your
Standard Library implementation and request that, in debug mode, they detect
this scenario and abort execution with a failed assertion.
--
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
--
---
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/.
g***@gmail.com
2015-04-24 23:33:42 UTC
Permalink
Post by Thiago Macieira
Post by g***@gmail.com
The issue isn't about performance, it's about reducing bugs related to
using move.
Then it's a QoI issue: please file a suggestion for improvement to your
Standard Library implementation and request that, in debug mode, they detect
this scenario and abort execution with a failed assertion.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
Quality is factor here - reducing likelihood of bugs - but first I don't
have any proven solution to suggest, That's why I'm discussing the issue
here to help evolve one with brighter minds than me.
But second, it's just plain slack to just say oh write to your vendor. How
does that help anybody else?

Let's say I did that, they'd just take that, augment it to their liking and
apply it to their library in some non standard way and who else benefits
from that? Not even me.

It would just encourage more of the proprietary designators we already have
today like the in/out and length type attributes that vendors already
litter their libraries with. But nobody else can use. These are
all features that might be useful to ones own libraries and types
and for cross platform code yet we can't use any of them because each
vendor has their own brand of it - probably because someone wrote to their
own vendor each time and we got a multitude of solutions that nobody can
use other than the vendor. So no, writing to my vendor is specifically NOT
want I want or plan to do.
--
---
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/.
Thiago Macieira
2015-04-25 16:40:55 UTC
Permalink
Post by g***@gmail.com
Quality is factor here - reducing likelihood of bugs - but first I don't
have any proven solution to suggest, That's why I'm discussing the issue
here to help evolve one with brighter minds than me.
But second, it's just plain slack to just say oh write to your vendor. How
does that help anybody else?
Let's say I did that, they'd just take that, augment it to their liking and
apply it to their library in some non standard way and who else benefits
from that? Not even me.
It would just encourage more of the proprietary designators we already have
today like the in/out and length type attributes that vendors already
litter their libraries with. But nobody else can use. These are
all features that might be useful to ones own libraries and types
and for cross platform code yet we can't use any of them because each
vendor has their own brand of it - probably because someone wrote to their
own vendor each time and we got a multitude of solutions that nobody can
use other than the vendor. So no, writing to my vendor is specifically NOT
want I want or plan to do.
My point was that the discussion seemed to wind down to the point where there
would be no changes to the standard: doing anything of what you propose would
be a performance hit for everyone who already has right code and has followed
the guidance since C++11 and move semantics came out.

So the next step is to see if you can ask your vendor to abort() execution in
debug mode. The solution would not be an extension: all you would need to do
to enable it would be to *not* define NDEBUG. Debug mode code often overwrites
buffers in free() with a known value and pre-initialises variables left
uninitialised with a known pattern too. So why not this?
--
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
--
---
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/.
Ross Smith
2015-04-24 03:01:50 UTC
Permalink
Post by g***@gmail.com
The difference is consistency - programmers mental model. Currently,
after a move (such as on a string) a types "valid but unspecified state
is" so unspecified that even my simple string example from earlier can't
be guaranteed to work i.e. even length cannot be guaranteed to be 0.
But because it feels natural that a string should be empty after a
move and because it usually does turn out to be in practice programmers
are making the mistake of relying on that. If you changed the
implementation of string to artificially change length to a random
number after move, I think a lot of code would break / you'd find a lot
of bugs.
If types could be generally said to reset to their default initialized
state this would fix some of those bugs and make a clearer mental model
as to what methods are valid after move, because if you are familiar
with a type to use it, you are familiar with it's default initialized
state to know what you can do with it.
The ideas was about attempting to make more methods valid after use a
move and an off the top of my head idea aimed at making the type of code
I already see "out there" like the string example that is currently
technically wrong, valid again.
There's a difference between invalid code and code that is valid but you
don't know what it will do. The standard guarantees that a moved-from
string "is left in a valid state with an unspecified value". That just
means it's an ordinary string whose contents you don't know; it doesn't
mean there's some sort of restriction on what you can do with it (aside
from operations that aren't guaranteed valid on every string anyway,
such as front()). In many ways a moved-from string is like a string
passed in to a function: you don't know what's in it, but you can trust
it to be, well, a string.

Consider this example:

void repchar(string& s, size_t n, char c) {
// Set the string to n copies of c
auto oldsize = s.size();
s.resize(n, c);
fill_n(s.begin(), min(n, oldsize), c);
}

(Yes, I know there's a version of string::assign() that already does
this, but I implemented it that way to make a point.)

Now suppose I write this code:

string s1, s2;
s1 = "Hello";
s2 = move(s1);
repchar(s1, 5, '!');
cout << s2 << s1 << '\n';

Can I trust that to print "Hello!!!!!"? Yes, I think I can, given the
current standard's spec for strings. The repchar() function never calls
anything like clear() that might be considered a "reset to a known
state" function, but repchar() can be expected to work perfectly well on
a string with arbitrary but known contents, so I think it can reasonably
be expected to work the same way on a moved-from string. A moved-from
string is just a string with unknown but valid content, not some kind of
delicate fragile half-constructed value that's only usable in certain
contexts.

I don't think there's anything wrong with the current specification of
move semantics, or that there's anything surprising about this
behaviour. Naively, I would expect a moved-from string to still contain
its original contents if it was short, but to be empty if it was long
(because of the short string optimization); I haven't tested this on any
real compiler. I don't see any problem with that behaviour, and would
not be happy if my compiler took it into its head to warn me about it,
or if some future iteration of the standard required less efficient
behaviour because some people thought it was "surprising".

(I believe the short vector optimization is not legal on std::vector,
because of the requirement that iterators remain valid after swap(), but
I don't think that applies to std::string.)

Ross Smith
--
---
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/.
g***@gmail.com
2015-04-24 12:09:19 UTC
Permalink
Post by Ross Smith
Post by g***@gmail.com
The difference is consistency - programmers mental model. Currently,
after a move (such as on a string) a types "valid but unspecified state
is" so unspecified that even my simple string example from earlier can't
be guaranteed to work i.e. even length cannot be guaranteed to be 0.
But because it feels natural that a string should be empty after a
move and because it usually does turn out to be in practice programmers
are making the mistake of relying on that. If you changed the
implementation of string to artificially change length to a random
number after move, I think a lot of code would break / you'd find a lot
of bugs.
If types could be generally said to reset to their default initialized
state this would fix some of those bugs and make a clearer mental model
as to what methods are valid after move, because if you are familiar
with a type to use it, you are familiar with it's default initialized
state to know what you can do with it.
The ideas was about attempting to make more methods valid after use a
move and an off the top of my head idea aimed at making the type of code
I already see "out there" like the string example that is currently
technically wrong, valid again.
There's a difference between invalid code and code that is valid but you
don't know what it will do. The standard guarantees that a moved-from
string "is left in a valid state with an unspecified value". That just
means it's an ordinary string whose contents you don't know; it doesn't
mean there's some sort of restriction on what you can do with it (aside
from operations that aren't guaranteed valid on every string anyway,
such as front()). In many ways a moved-from string is like a string
passed in to a function: you don't know what's in it, but you can trust
it to be, well, a string.
void repchar(string& s, size_t n, char c) {
// Set the string to n copies of c
auto oldsize = s.size();
s.resize(n, c);
fill_n(s.begin(), min(n, oldsize), c);
}
(Yes, I know there's a version of string::assign() that already does
this, but I implemented it that way to make a point.)
string s1, s2;
s1 = "Hello";
s2 = move(s1);
repchar(s1, 5, '!');
cout << s2 << s1 << '\n';
Can I trust that to print "Hello!!!!!"? Yes, I think I can, given the
current standard's spec for strings.
I don't think you can. But if I'm wrong about that it wouldn't change my
view point. But it might change yours.
There is no guarantee what size() would be. That's certainly what others
have concurred with at the start of this discussion.
Post by Ross Smith
The repchar() function never calls
anything like clear() that might be considered a "reset to a known
state" function, but repchar() can be expected to work perfectly well on
a string with arbitrary but known contents, so I think it can reasonably
be expected to work the same way on a moved-from string. A moved-from
string is just a string with unknown but valid content, not some kind of
delicate fragile half-constructed value that's only usable in certain
contexts.
I don't think there's anything wrong with the current specification of
move semantics,
I've been clear to say all along that I don't think there is anything wrong
with move semantics either.
You seem to be missing what I've said.
I think you aren't focused on the issue you are focused on whether my off
the top of my head ideas as possible solution directions to the issue are
correct at the expense of the issue.
I recommend focus on the issue I am talking about and maybe you'll have a
better idea there. If there is no solution then fine.
But trying to improve X doesn't mean that X is wrong. It just means asking
the question can X be better.
There's nothing wrong with delete either, but it didn't stop being trying
to find ways of improving things so that use after delete could be found.
Post by Ross Smith
or that there's anything surprising about this
behaviour. Naively, I would expect a moved-from string to still contain
its original contents if it was short, but to be empty if it was long
(because of the short string optimization); I haven't tested this on any
real compiler. I don't see any problem with that behaviour, and would
not be happy if my compiler took it into its head to warn me about it,
or if some future iteration of the standard required less efficient
behaviour because some people thought it was "surprising".
(I believe the short vector optimization is not legal on std::vector,
because of the requirement that iterators remain valid after swap(), but
I don't think that applies to std::string.)
Ross Smith
--
---
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-04-22 04:21:49 UTC
Permalink
Are you saying that this output is undefined, i.e. that on another machine the output might be something different?
Yes. Not quite undefined, but in a valid but unspecified state.
More specifically, std::string may implement the small string optimization.
the standard should change to make such code work as expected for things like string.
e.g.: guarantee this assert will never fail.
std::string s1 = "hello";
std::string s2 = std::move(s)
assert(s1.empty());
For std::vector (over std::allocator), as far as I know, the moved-from state is constrained to be empty by the requirement that iterators to the moved-from object remain valid, pointing to the moved-to object.
--
---
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/.
Nevin Liber
2015-04-22 05:55:27 UTC
Permalink
Post by David Krauss
For std::vector (over std::allocator), as far as I know, the moved-from
state is constrained to be empty by the requirement that iterators to the
moved-from object remain valid, pointing to the moved-to object.
That may be true, which means we probably should change the note (even
though it is non-normative) with a better example.
--
Nevin ":-)" Liber <mailto:***@eviloverlord.com> (847) 691-1404
--
---
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/.
Vicente J. Botet Escriba
2015-04-24 06:01:36 UTC
Permalink
Post by Nathan Ernst
(This question is based upon document ISO/IEC 14882:2011(E))
I'm writing a parser that's going to end up generating lots of
instances of standard strings and vectors, and I'm trying to be as
unobtrusive in terms of memory as I can. I'm trying to move the
generated strings and vectors for obvious reasons, but it got me
thinking about the state of the the carcass of the moved instance.
For strings, I found the language in § 21.4.2 paragraph 2 that "In the
second form, str is left in a valid state with an unspecified value."
The second form refers to the the rvalue-reference constructor of
basic_string.
From this language, I expect I should be able to call methods on a
previously moved instance without introducing undefined behavior, but
the result of those calls are unspecified (calling c_str could return
an empty string or anything else). What I gather from this is that if
I wanted to reuse a previously moved string instance, I would need
re-initialize it in order to get to a known state.
std::function<void(std::string&&)> sink = ...;
std::string s;
for (int i = 0; i < 26; ++i)
{
s.append('a' + i);
sink(std::move(s));
}
It would be conformant for sink to receive "a", "b", "c".... or to
receive "a", "ab", "abc", or really anything else after initially
receiving "a".
Question boils down to: To be in a known & valid state, do I need to
reinitialize a moved type in order to be able to guarantee behavior?
i.e. Do I need to do the following in order to ensure sink receives
std::function<void(std::string&&)> sink = ...;
std::string s;
for (int i = 0; i < 26; ++i)
{
s.append('a' + i);
sink(std::move(s));
s = std::string(); // possibly redundant?
}
In general would this assumption hold across all standard movable
types? Is the extra re-initialization necessary to ensure cross
platform/implementation behavior?
Hi,

you could do

std::function<void(std::string&&)> sink = ...;


for (int i = 0; i < 26; ++i)
{
std::string s; = ('a' + i);
sink(std::move(s));

}


Anyway, the problem of portability should be solved with Contracts.
Contracts will show you in one way or another that your code is invalid.

Vicente
--
---
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/.
Continue reading on narkive:
Loading...