Topic: Defect Report: Wordings on basic_string::data() disallow
Author: =?windows-1252?Q?Daniel_Kr=FCgler?=<daniel.kruegler@googlemail.com>
Date: Wed, 24 Aug 2011 17:38:52 -0700 (PDT) Raw View
On 2011-08-24 03:56, Kazutoshi Satoda wrote:
> In N3290 (2011 FDIS), C.2.11 [diff.cpp03.strings] says:
>> 21.4.1
>> Change: Loosen basic_string invalidation rules
>> Rationale: Allow small-string optimization.
>
> Here is an existing implementation (STLport) with such optimization.
> http://stlport.git.sourceforge.net/git/gitweb.cgi?p=stlport/stlport;a=blob;f=stlport/stl/_string_base.h;hb=af309b7a853f95ae8d4546ac79525d6a2e1fa450#l55
>
> Then, 21.4.2 [string.cons] Table 71 says:
>> Table 71 operator=(const basic_string<charT, traits, Allocator>&&) effects
>>
>> data(): points at the array whose first element was pointed at by str.data()
>
> I read the above wording of effect on data() as it mandates the
> following assertion to always succeed.
>
> void f(std::string& x, std::string&& str)
> {
> char const* const was_pointed = str.data();
> x = std::move(str);
> assert(x.data() == was_pointed);
> }
>
> But it can fail if small-string optimization is implemented and str
> was short enough.
You cannot make normative conclusions from this code, because it invokes
undefined behaviour. This is so, because the expression
x = std::move(str)
calls a function that (potentially) invalidates the pointer returned
fromstr.data(), based on 21.4.1 [string.require] p6:
"References, pointers, and iterators referring to the elements of a
basic_string sequence may be invalidated by the following uses of that
basic_string object:
as an argument to any standard library function taking a reference to
non-const basic_string as an argument.(footnote)
Calling non-const member functions, except operator[], at, front,
back, begin, rbegin, end, and rend."
> Additionally, but less obviously, many tables in 21.4.2 says that data()
> should point at the first element of "an allocated copy". I think
> "allocated" should be dropped not to imply a probable call to
> Allocator::allocate() which can be optional with small-string
> optimization.
I agree, this will be forwarded to LWG.
> I think these wordings of effects on data() are unintentionally
> over-constraining, and should be relaxed to keep the rationale for
> C.2.11 "Allow small-string optimization" really valid.
I have not yet seen an example which proves this. Your above given
example is invalid.
HTH& Greetings from Bremen,
- Daniel Kr gler
--
[ comp.std.c++ is moderated. To submit articles, try posting with your ]
[ newsreader. If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Kazutoshi Satoda <k_satoda@f2.dion.ne.jp>
Date: Sun, 28 Aug 2011 08:42:08 -0700 (PDT) Raw View
The original subject was:
Defect Report: Wordings on basic_string::data() disallow small-string
optimization
The last "optimization" was wrapped and dropped from subject for some
reason (80 columns?).
Daniel Kr gler wrote:
>
> On 2011-08-24 03:56, Kazutoshi Satoda wrote:
>>
>> void f(std::string& x, std::string&& str)
>> {
>> char const* const was_pointed = str.data();
>> x = std::move(str);
>> assert(x.data() == was_pointed);
>> }
(snip)
>
> You cannot make normative conclusions from this code, because it invokes
> undefined behaviour. This is so, because the expression
>
> x = std::move(str)
>
> calls a function that (potentially) invalidates the pointer returned
> fromstr.data(), ...
Then, replace the example with this one:
void f(std::string& x, std::string&& str)
{
char const* const was_pointed = str.data();
x = static_cast<std::string&&>(str);
assert(x.data() == was_pointed);
}
>> I think these wordings of effects on data() are unintentionally
>> over-constraining, and should be relaxed to keep the rationale for
>> C.2.11 "Allow small-string optimization" really valid.
>
> I have not yet seen an example which proves this. Your above given
> example is invalid.
I believe the above revised example is now good to show the problem.
--
k_satoda
[ comp.std.c++ is moderated. To submit articles, try posting with your ]
[ newsreader. If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Kazutoshi Satoda <k_satoda@f2.dion.ne.jp>
Date: Sun, 28 Aug 2011 08:42:30 -0700 (PDT) Raw View
> You cannot make normative conclusions from this code, because it invokes
> undefined behaviour. This is so, because the expression
>
> x = std::move(str)
>
> calls a function that (potentially) invalidates the pointer returned
> fromstr.data(), based on 21.4.1 [string.require] p6:
>
> "References, pointers, and iterators referring to the elements of a
> basic_string sequence may be invalidated by the following uses of that
> basic_string object:
> as an argument to any standard library function taking a reference to
> non-const basic_string as an argument.(footnote)
> Calling non-const member functions, except operator[], at, front,
> back, begin, rbegin, end, and rend."
In the previous reply, I took "standard library function taking a
reference to ..." as std::move() in the above expression.
But if you are referring operator=(basic_string&&) itself as the
trigger of invalidation, it seems impossible to show the problem with a
valid C++ code example, because comparison of pointers is the only way
in C++ to examine the effect on data().
>
> data(): points at the array whose first element was pointed at by str.data()
Returning to the original DR;
It is not possible to confirm the above effect of data() if small-string
optimization is implemented, because data() may point an small array
which is distinct for each basic_string instance to decrease the number
of allocation.
--
k_satoda
[ comp.std.c++ is moderated. To submit articles, try posting with your ]
[ newsreader. If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: =?UTF-8?B?RGFuaWVsIEtyw7xnbGVy?=<daniel.kruegler@googlemail.com>
Date: Sun, 28 Aug 2011 11:06:57 -0700 (PDT) Raw View
Am 28.08.2011 17:42, schrieb Kazutoshi Satoda:
> Daniel Kr gler wrote:
>>
>> On 2011-08-24 03:56, Kazutoshi Satoda wrote:
>>>
>>> void f(std::string& x, std::string&& str)
>>> {
>>> char const* const was_pointed = str.data();
>>> x = std::move(str);
>>> assert(x.data() == was_pointed);
>>> }
>
> (snip)
>>
>> You cannot make normative conclusions from this code, because it invokes
>> undefined behaviour. This is so, because the expression
>>
>> x = std::move(str)
>>
>> calls a function that (potentially) invalidates the pointer returned
>> fromstr.data(), ...
>
> Then, replace the example with this one:
>
> void f(std::string& x, std::string&& str)
> {
> char const* const was_pointed = str.data();
> x = static_cast<std::string&&>(str);
> assert(x.data() == was_pointed);
> }
This variation does not change the situation. The problem was not the
call of std::move, but the call of the non-const member function (the
move assignment operator of std::string) in the expression
x = static_cast<std::string&&>(str);
The library specification does not require that the pointer obtained in
the previous line
char const* const was_pointed = str.data();
is still valid after the move-assignment. Thus, after the
move-assignement, the pointer obtained from str.data() is potentially
invalid. Note also, that the same restrictions apply to the member or
non-member swap function for basic_string. The footnote 234 in N3290
makes that undoubtedly clear:
"For example, as an argument to non-member functions swap() (21.4.8.8),
operator>>() (21.4.8.9), and getline() (21.4.8.9), or as an argument to
basic_string::swap()"
>>> I think these wordings of effects on data() are unintentionally
>>> over-constraining, and should be relaxed to keep the rationale for
>>> C.2.11 "Allow small-string optimization" really valid.
>>
>> I have not yet seen an example which proves this. Your above given
>> example is invalid.
>
> I believe the above revised example is now good to show the problem.
It is not.
HTH& Greetings from Bremen,
- Daniel Kr gler
--
[ comp.std.c++ is moderated. To submit articles, try posting with your ]
[ newsreader. If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: =?UTF-8?B?RGFuaWVsIEtyw7xnbGVy?=<daniel.kruegler@googlemail.com>
Date: Sun, 28 Aug 2011 11:07:42 -0700 (PDT) Raw View
Am 28.08.2011 17:42, schrieb Kazutoshi Satoda:
>> You cannot make normative conclusions from this code, because it invokes
>> undefined behaviour. This is so, because the expression
>>
>> x = std::move(str)
>>
>> calls a function that (potentially) invalidates the pointer returned
>> fromstr.data(), based on 21.4.1 [string.require] p6:
>>
>> "References, pointers, and iterators referring to the elements of a
>> basic_string sequence may be invalidated by the following uses of that
>> basic_string object:
>> as an argument to any standard library function taking a reference to
>> non-const basic_string as an argument.(footnote)
>> Calling non-const member functions, except operator[], at, front,
>> back, begin, rbegin, end, and rend."
>
> In the previous reply, I took "standard library function taking a
> reference to ..." as std::move() in the above expression.
Yes.
> But if you are referring operator=(basic_string&&) itself as the
> trigger of invalidation,
I did.
> it seems impossible to show the problem with a
> valid C++ code example, because comparison of pointers is the only way
> in C++ to examine the effect on data().
>>
>> data(): points at the array whose first element was pointed at by
>> str.data()
>
> Returning to the original DR;
> It is not possible to confirm the above effect of data() if small-string
> optimization is implemented, because data() may point an small array
> which is distinct for each basic_string instance to decrease the number
> of allocation.
What I'm trying to say is that you cannot prove a defect unless you
still try to induce conclusions about the standard or implications on
implementations by providing code examples that invoke undefined
behaviour. And your examples did always induce undefined behaviour yet,
so there is nothing they can really point out. Note that "undefined
behaviour" does not require that crude things might happen on a machine,
undefined behaviour can in fact be a very defined behaviour for some
specific platforms. There is another way of looking at the wording state
for std::basic_string: The standard says that the invalidation happens
such to *ensure* that the short-string optimization is possible, it does
not *require* an invalidation in the sense of requiring the production
of an invalid pointer value.
For the same reason the quoted wording is written such that a call of a
swap function for basic_string (member and non-member) potentially
invalidates any pointer, reference, and iterator taken before. If the
wording state wouldn't be as written, you could construct another
example like this one
void g(std::string& x, std::string& y)
{
char const* const was_pointed = y.data();
x.swap(y);
assert(x.data() == was_pointed);
}
and you could now assert that this example also demonstrates that the
standard disallows the short-string optimization. But this example is
also invalid as any of your examples shown so far.
HTH& Greetings from Bremen,
Daniel Kr gler
--
[ comp.std.c++ is moderated. To submit articles, try posting with your ]
[ newsreader. If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]