string_view and rvalue strings

505 views
Skip to first unread message

jgot...@gmail.com

unread,
Nov 11, 2013, 10:18:16 PM11/11/13
to std-pr...@isocpp.org
As the string_view class is currently written, the following code will cause an access to a destructed object and probably a crash:

std::string foo();

int main() {
    string_view view
(foo(), 0, 3);
    std
::cout << view;
}

When the string_view is created, it stores pointers into the temporary string created by foo(), which is then destroyed, leaving the string_view with pointers to garbage. I know that if the string_view were immediately sent to cout without creating an lvalue everything would have been fine.  However, this seems dangerous enough that it might be a good idea to forbid creating a string_view from an rvalue std::string, by deleting the constructor and assignment operator from rvalue strings.

Joe Gottman

Billy O'Neal

unread,
Nov 11, 2013, 10:20:37 PM11/11/13
to std-proposals
There are already plenty of things you can do to reference a destructed object, e.g.
 
char * view = foo().c_str();
 
Users using a specialized class like string_view should understand what it means.

Billy O'Neal
Malware Response Instructor - BleepingComputer.com


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

Zhihao Yuan

unread,
Nov 11, 2013, 11:22:14 PM11/11/13
to std-pr...@isocpp.org
On Mon, Nov 11, 2013 at 10:18 PM, <jgot...@gmail.com> wrote:
> However, this seems dangerous enough that it might be a good idea to forbid
> creating a string_view from an rvalue std::string, by deleting the
> constructor and assignment operator from rvalue strings.

I +1. The generic reference semantics adaptor reference_wrapper
already does this.

--
Zhihao Yuan, ID lichray
The best way to predict the future is to invent it.
___________________________________________________
4BSD -- http://4bsd.biz/

Alex B

unread,
Nov 12, 2013, 12:48:42 AM11/12/13
to std-pr...@isocpp.org
Reference wrapper is not meant to be used in all the same contexts. For me, one of the major selling point for string_view is that it should most of the time be used as a function parameter.

string foo();

void bar(string_view s);

void f()
{
    bar
(foo()); // this is perfectly valid; are you suggesting not allowing this?
}

I understand your fears that providing a constructor taking an rvalue can be dangerous in some use cases, but not providing it just seems annoying in the most obvious cases.

Daniel Krügler

unread,
Nov 12, 2013, 1:10:08 AM11/12/13
to std-pr...@isocpp.org
2013/11/12 <jgot...@gmail.com>:
Yes, I very much agree with that suggestion. Besides reference_wrapper
(as denoted by Zhihao Yuan), we have similar places where we have
added (or are considering to add) "rvalue-protection", one of the most
recent proposals are

http://cplusplus.github.io/LWG/lwg-active.html#2329
http://cplusplus.github.io/LWG/lwg-active.html#2332

- Daniel

Billy O'Neal

unread,
Nov 12, 2013, 1:33:34 AM11/12/13
to std-proposals
This. A thousand times this.

Billy O'Neal
Malware Response Instructor - BleepingComputer.com


--

xavi

unread,
Nov 12, 2013, 4:01:08 AM11/12/13
to std-proposals
As Alex points out, this disallows many legitimate (and useful) uses of string_view. This would be very easily solvable if we had ref-qualified constructors:

string_view(const string&) { ... }
string_view(string&&) & = delete;
string_view(string&& s) && : string_view(s) {}

which disallows the "bad" use cases while allowing the useful ones.
Which was the reason for not allowing ref-qualification of constructors in C++11? Has there been any proposal to introduce them?



2013/11/12 Billy O'Neal <billy...@gmail.com>

Daniel Krügler

unread,
Nov 12, 2013, 4:05:23 AM11/12/13
to std-pr...@isocpp.org
2013/11/12 xavi <gra...@gmail.com>:
> Which was the reason for not allowing ref-qualification of constructors in
> C++11? Has there been any proposal to introduce them?

You need to explain the specific semantics of this qualification:
Constructors don't act on an existing object, so neither
cv-qualification nor ref-qualifiers make sense with the existing
meaning of such qualifications.

- Daniel

xavi

unread,
Nov 12, 2013, 4:39:01 AM11/12/13
to std-proposals
I have to admit I haven't given it a lot of thought, but right now three ways to invoke a constructor (other than delegating constructors or constructors of base classes, which should have the same refness as the constructor from which they are invoked) come to mind:

some_class object(...); //obviously an lvalue, since we are constructing a named object
foo(some_class(...)); //obviously an rvalue, since we are constructing a temporary
new some_class(...); //also an lvalue, since any dereference of the pointer will yield an lvalue reference

am I missing something? I don't think that any of these cases would be confusing for anyone.



2013/11/12 Daniel Krügler <daniel....@gmail.com>

Ville Voutilainen

unread,
Nov 12, 2013, 4:42:27 AM11/12/13
to std-pr...@isocpp.org
On 12 November 2013 11:39, xavi <gra...@gmail.com> wrote:
> I have to admit I haven't given it a lot of thought, but right now three
> ways to invoke a constructor (other than delegating constructors or
> constructors of base classes, which should have the same refness as the
> constructor from which they are invoked) come to mind:
>
> some_class object(...); //obviously an lvalue, since we are constructing a
> named object
> foo(some_class(...)); //obviously an rvalue, since we are constructing a
> temporary
> new some_class(...); //also an lvalue, since any dereference of the pointer
> will yield an lvalue reference
>
> am I missing something? I don't think that any of these cases would be
> confusing for anyone.

How did you imagine implementing this knowledge of construction context,
aka the lvalueness, in constructors? Pass a flag internally? That will likely
be a serious ABI break.

Xavi

unread,
Nov 12, 2013, 4:52:42 AM11/12/13
to std-pr...@isocpp.org
I don't understand how it is any different from other ref-qualified member functions. There are two different constructors (when they are actually implemented separately) and the compiler selects one of them. Was it not ABI-breaking to implement ref-qualification in the first place?

Ville Voutilainen

unread,
Nov 12, 2013, 6:12:43 AM11/12/13
to std-pr...@isocpp.org
On 12 November 2013 11:52, Xavi <gra...@gmail.com> wrote:
> I don't understand how it is any different from other ref-qualified member functions. There are two different constructors (when they are actually implemented separately) and the compiler selects one of them. Was it not ABI-breaking to implement ref-qualification in the first place?

The other ref-qualified member functions already have a parameter they
can use, namely 'this'.

Having said that, I don't suppose the idea of having ref-qualified
constructors is that much different
from having in-charge constructors and not-in-charge constructors (which are
used for delegating cases, a delegation target that is not a
constructor of a most-derived-class
is a not-in-charge constructor and doesn't construct a virtual base,
for example).
Even so, that certainly seems like an additional parameter hidden
inside the implementation,
so it probably has ABI effects.

xavi

unread,
Nov 12, 2013, 6:44:56 AM11/12/13
to std-proposals
I still don't see the difference. Constructors also have the 'this' parameter, since they don't allocate the memory for the object, they merely manipulate it, just like any other member function. I'm not saying it doesn't have ABI effects, I'm just saying that it should have the same effects that introducing ref-qualified member functions had.


2013/11/12 Ville Voutilainen <ville.vo...@gmail.com>

Ville Voutilainen

unread,
Nov 12, 2013, 6:52:57 AM11/12/13
to std-pr...@isocpp.org
On 12 November 2013 13:44, xavi <gra...@gmail.com> wrote:
> I still don't see the difference. Constructors also have the 'this'
> parameter, since they don't allocate the memory for the object, they merely
> manipulate it, just like any other member function. I'm not saying it
> doesn't have ABI effects, I'm just saying that it should have the same
> effects that introducing ref-qualified member functions had.

Ok, seems plausible.

Would a ref-qualified constructor solve the problem? Having
string_view(string&& s) && : string_view(s) {}
doesn't guarantee the string_view has a shorter lifetime than the
rvalue string it's viewing.

Ville Voutilainen

unread,
Nov 12, 2013, 6:56:08 AM11/12/13
to std-pr...@isocpp.org
On 12 November 2013 13:52, Ville Voutilainen
To elaborate: which constructor would this call:

string_view&& yay_lifetime_extension = string_view(string());

If it invokes the lvalue one, we're safe again since that's deleted.
So ok so far.
The idea of this facility certainly seems interesting.

xavi

unread,
Nov 12, 2013, 7:11:49 AM11/12/13
to std-proposals
Yes, that should be an exception and call the lvalue constructor. The whole statement is somewhat exceptional in that it extends the lifetime of a temporary, making it not temporary anymore.
There is another hard to handle case:

string_view foo()
{
    return string_view(string());
}

This should call the rvalue constructor, which makes sense, and immediately end up with a dangling reference. I don't see any reason why it couldn't be made to call the lvalue constructor, since it is equivalent to:

string_view a(string());
return a;

which would use the lvalue constructor. This could be used to avoid lots of dangling references.
The whole thing would still blow up with:

string_view foo()
{
    string a;
    return string_view(a);
}

If we want C++ to be as flexible as it is, there will always be ways to end up with dangling references. I think ref-qualifying constructors could help diagnose some of them.


2013/11/12 Ville Voutilainen <ville.vo...@gmail.com>

Ville Voutilainen

unread,
Nov 12, 2013, 7:22:00 AM11/12/13
to std-pr...@isocpp.org
On 12 November 2013 14:11, xavi <gra...@gmail.com> wrote:
> Yes, that should be an exception and call the lvalue constructor. The whole
> statement is somewhat exceptional in that it extends the lifetime of a
> temporary, making it not temporary anymore.
> There is another hard to handle case:
>
> string_view foo()
> {
> return string_view(string());
> }
>
> This should call the rvalue constructor, which makes sense, and immediately
> end up with a dangling reference. I don't see any reason why it couldn't be
> made to call the lvalue constructor, since it is equivalent to:
>
> string_view a(string());
> return a;
>
> which would use the lvalue constructor. This could be used to avoid lots of
> dangling references.

I certainly think it could be made to call the lvalue ctor, the
language rules for that
are already in place, it's a local temporary and elision rules apply,
so it shouldn't
be hard to call the lvalue ctor there. I'm talking about the rule that
allows return
to move automatically.

> The whole thing would still blow up with:
>
> string_view foo()
> {
> string a;
> return string_view(a);
> }

Same thing, local temporary, elision rules apply, I think we could make that
call the lvalue ctor.

Now the question is whether the problem is common enough to have to explain
this to users, and to specify and implement such a new rule. I don't
want to predict
that yet.

xavi

unread,
Nov 12, 2013, 7:32:33 AM11/12/13
to std-proposals



2013/11/12 Ville Voutilainen <ville.vo...@gmail.com>
Yes, it would call the lvalue constructor, but it would call it with an lvalue as a parameter, so there is no way to stop this from compiling. But people should know not to return references to temporaries, and string_view is a reference.

Now the question is whether the problem is common enough to have to explain
this to users, and to specify and implement such a new rule. I don't
want to predict
that yet.

I don't think it's harder than, for example, the rules for reference collapsing, or that a named rvalue reference is actually an lvalue. They are things that most users need not care about. They should just know that you shouldn't construct non-temporary string_views from temporary strings. This is something they will need to know whether we find a way to actually forbid it or not. All this would introduce is a way to enforce good practice. 

Ville Voutilainen

unread,
Nov 12, 2013, 7:40:10 AM11/12/13
to std-pr...@isocpp.org
On 12 November 2013 14:32, xavi <gra...@gmail.com> wrote:
>> > string_view foo()
>> > {
>> > string a;
>> > return string_view(a);
>> > }
>>
>> Same thing, local temporary, elision rules apply, I think we could make
>> that
>> call the lvalue ctor.
>>
> Yes, it would call the lvalue constructor, but it would call it with an
> lvalue as a parameter, so there is no way to stop this from compiling. But
> people should know not to return references to temporaries, and string_view
> is a reference.

Well, there is a way, but we would begin to require implementations to track
both lifetimes, and even that doesn't necessarily do the right thing
and we can't
know whether the user intended the lvalue-ctor-with-rvalue-arg or
something else.

>
>> Now the question is whether the problem is common enough to have to
>> explain
>> this to users, and to specify and implement such a new rule. I don't
>> want to predict
>> that yet.
>>
> I don't think it's harder than, for example, the rules for reference
> collapsing, or that a named rvalue reference is actually an lvalue. They are
> things that most users need not care about. They should just know that you
> shouldn't construct non-temporary string_views from temporary strings. This
> is something they will need to know whether we find a way to actually forbid
> it or not. All this would introduce is a way to enforce good practice.

What certainly gives me pause is invoking the lvalue constructor for something
that is clearly a temporary, even though that pause isn't long.

Perhaps this can be solved by implementation diagnostics. That may not be
a perfect solution, but it's certainly a more light-weight solution that a whole
new facility, as intriguing a facility it may be.

David Krauss

unread,
Nov 12, 2013, 7:46:44 AM11/12/13
to std-pr...@isocpp.org
Are you less opposed to ref-qualified constructors than destructors? The
latter also may have an in-charge mode to support dynamic type
semantics. Destructors make more sense to me because it's more obvious
that the object has a name at the end of its scope, although I suppose a
name bound to a temporary refers to its storage which exists from just
before the beginning of its lifetime.

Either way, having a single type with both overloads would be fairly
bizarre. Seems to imply tracking compile-time information at runtime.

Also, string_view looks more like an application of a passive,
non-allocating allocator, or a generic container proxy, than a distinct
class.

Ville Voutilainen

unread,
Nov 12, 2013, 7:52:40 AM11/12/13
to std-pr...@isocpp.org
Yes, I am, especially if that results in programmers writing multiple
destructors.
They already have multiple constructors, and delegating constructors solve
some of the related problems. I feel queasy about multiple destructors
and delegation in them, but I suppose delegating destructors would be
palatable, if a bit hard to swallow.

I think I would much prefer leaving the detection of possible problems with
classes such as string_view to implementations, and tell the programmers
to be careful with what they do, all in all.

Zhihao Yuan

unread,
Nov 12, 2013, 9:04:18 AM11/12/13
to std-pr...@isocpp.org
On Tue, Nov 12, 2013 at 12:48 AM, Alex B <deva...@gmail.com> wrote:
> Reference wrapper is not meant to be used in all the same contexts. For me,
> one of the major selling point for string_view is that it should most of the
> time be used as a function parameter.
>
> string foo();
>
> void bar(string_view s);
>
> void f()
> {
> bar(foo()); // this is perfectly valid; are you suggesting not allowing
> this?
> }

Ah, I was shot down. reference_wrapper has std::cref,
but string_view does not, and can not :(

Olaf van der Spek

unread,
Dec 26, 2013, 3:00:03 PM12/26/13
to std-pr...@isocpp.org, jgot...@gmail.com
Wouldn't this disallow directly passing the output of foo() into a function taking a string_view? That'd suck. 
 
Reply all
Reply to author
Forward
0 new messages