Jump to page: 1 2
Thread overview
Example of the perils of binding rvalues to const ref
Sep 16, 2014
monarch_dodra
Sep 16, 2014
Sean Kelly
Sep 17, 2014
Kagamin
Sep 17, 2014
Arjan
Sep 17, 2014
Szymon Gatner
Sep 17, 2014
Szymon Gatner
Sep 17, 2014
IgorStepanov
Sep 17, 2014
IgorStepanov
Sep 18, 2014
monarch_dodra
Sep 18, 2014
IgorStepanov
Sep 18, 2014
IgorStepanov
Sep 17, 2014
Olivier Grant
September 16, 2014
http://www.slideshare.net/yandex/rust-c

C++ code:

std::string get_url() {
    return "http://yandex.ru";
}

string_view get_scheme_from_url(string_view url) {
    unsigned colon = url.find(':');
    return url.substr(0, colon);
}

int main() {
    auto scheme = get_scheme_from_url(get_url());
    std::cout << scheme << "\n";
    return 0;
}

string_view has an implicit constructor from const string& (see "basic_string_view(const basic_string<charT, traits, Allocator>& str) noexcept;" in https://isocpp.org/files/papers/N3762.html). The function get_url() returns an rvalue, which in turn gets bound to a reference to const and implicitly passed to string_view's constructor. The obtained view refers to a dead string.


Andrei
September 16, 2014
On Tuesday, 16 September 2014 at 15:30:49 UTC, Andrei Alexandrescu wrote:
> http://www.slideshare.net/yandex/rust-c
>
> C++ code:
>
> std::string get_url() {
>     return "http://yandex.ru";
> }
>
> string_view get_scheme_from_url(string_view url) {
>     unsigned colon = url.find(':');
>     return url.substr(0, colon);
> }
>
> int main() {
>     auto scheme = get_scheme_from_url(get_url());
>     std::cout << scheme << "\n";
>     return 0;
> }
>
> string_view has an implicit constructor from const string& (see "basic_string_view(const basic_string<charT, traits, Allocator>& str) noexcept;" in https://isocpp.org/files/papers/N3762.html). The function get_url() returns an rvalue, which in turn gets bound to a reference to const and implicitly passed to string_view's constructor. The obtained view refers to a dead string.
>
>
> Andrei

Arguably, the issue is not const ref binding to an rvalue itself, but rather taking (and *holding*) the address of a parameter that is passed by const ref. If you want to *hold* that reference, it should be explicitly passed by pointer. That and having the whole thing neatly packaged in an implicit constructor. If you are doing something that dangerous, at the very least, make it explicit.

I mean, the example might as well just be:

std::string_view get_scheme()
{
    std::string myString = get_url();
    return myString; //Boom
}

Exact same undefined result, without binding to rvalues.

I prefered your smoking gun of:

const int& a = max(1, 2);

But again, the part of the issue here is the passing of references.

If we made "auto ref" to mean "pass either an existing object, or binds to an rvalue (at call site, not via template overload)" and in the implementation, made the passed in argument "considered a local variable as if passed by value you may not escape", then I'm pretty sure we can have our cake and eat it. Proper escape analysis would help too.
September 16, 2014
On Tuesday, 16 September 2014 at 15:30:49 UTC, Andrei
Alexandrescu wrote:
> http://www.slideshare.net/yandex/rust-c
>
> C++ code:
>
> std::string get_url() {
>     return "http://yandex.ru";
> }
>
> string_view get_scheme_from_url(string_view url) {
>     unsigned colon = url.find(':');
>     return url.substr(0, colon);
> }
>
> int main() {
>     auto scheme = get_scheme_from_url(get_url());
>     std::cout << scheme << "\n";
>     return 0;
> }
>
> string_view has an implicit constructor from const string& (see "basic_string_view(const basic_string<charT, traits, Allocator>& str) noexcept;" in https://isocpp.org/files/papers/N3762.html). The function get_url() returns an rvalue, which in turn gets bound to a reference to const and implicitly passed to string_view's constructor. The obtained view refers to a dead string.

That's a design mistake on someone's part.  It shouldn't be legal
to construct a view of an implicitly created temporary.  Or at
least not to escape it.

As a more straightforward example of the same problem, I ran into
a similar issue when creating constructor functions for a slice
class I use for my own work.  Those functions can't accept a
const ref to a std::string, because you can end up with this:

slice<char const> wrap(std::string const &s) {
     return slice<char const>(s.c_str(), s.length());
}

slice<char const> s = wrap("abc"); // s now aliases a discarded
temporary

I had to choose my overloads very carefully to avoid these issues.

In short, these problems crop up the moment you start aliasing
objects in C++.  To be honest, I'm amazed that the STL contains
something like string_view.  What kind of guarantees is it
supposed to provide?
September 17, 2014
On 9/16/14, 11:26 AM, monarch_dodra wrote:
> Arguably, the issue is not const ref binding to an rvalue itself, but
> rather taking (and *holding*) the address of a parameter that is passed
> by const ref

It doesn't do that. It holds pointer/length to the slice, which is different and distinct from the pointer to the string. -- Andrei
September 17, 2014
On Tuesday, 16 September 2014 at 15:30:49 UTC, Andrei Alexandrescu wrote:
> The function get_url() returns an rvalue, which in turn gets bound to a reference to const and implicitly passed to string_view's constructor. The obtained view refers to a dead string.

When I saw this idiom used in D, I asked the same question: how long the temporary should survive? Does it work more reliably in D? It would be ok if it survives till the end of the scope, optimization opportunities lost this way should be small enough.
September 17, 2014
On Tuesday, 16 September 2014 at 15:30:49 UTC, Andrei Alexandrescu wrote:
> http://www.slideshare.net/yandex/rust-c
>
> C++ code:
>
> std::string get_url() {
>     return "http://yandex.ru";
> }
>
> string_view get_scheme_from_url(string_view url) {
>     unsigned colon = url.find(':');
>     return url.substr(0, colon);
> }
>
> int main() {
>     auto scheme = get_scheme_from_url(get_url());
>     std::cout << scheme << "\n";
>     return 0;
> }
>
> string_view has an implicit constructor from const string& (see "basic_string_view(const basic_string<charT, traits, Allocator>& str) noexcept;" in https://isocpp.org/files/papers/N3762.html). The function get_url() returns an rvalue, which in turn gets bound to a

Forgive me my ignorance but  get_url() returns a std::string (on the stack), so its address can be token.
And iirc the explainer Scott Meyers explained once "iff you can take its address its not a rvalue its a lvalue". So isn't the get_scheme_from_url() not simply holding a const ref to temporary? (which most compiler warn about)

...Or am I missing the point?

> reference to const and implicitly passed to string_view's constructor. The obtained view refers to a dead string.
>
>
> Andrei

September 17, 2014
On Wednesday, 17 September 2014 at 08:52:58 UTC, Arjan wrote:
> On Tuesday, 16 September 2014 at 15:30:49 UTC, Andrei Alexandrescu wrote:
>> http://www.slideshare.net/yandex/rust-c
>>
>> C++ code:
>>
>> std::string get_url() {
>>    return "http://yandex.ru";
>> }
>>
>> string_view get_scheme_from_url(string_view url) {
>>    unsigned colon = url.find(':');
>>    return url.substr(0, colon);
>> }
>>
>> int main() {
>>    auto scheme = get_scheme_from_url(get_url());
>>    std::cout << scheme << "\n";
>>    return 0;
>> }
>>
>> string_view has an implicit constructor from const string& (see "basic_string_view(const basic_string<charT, traits, Allocator>& str) noexcept;" in https://isocpp.org/files/papers/N3762.html). The function get_url() returns an rvalue, which in turn gets bound to a
>
> Forgive me my ignorance but  get_url() returns a std::string (on the stack), so its address can be token.
> And iirc the explainer Scott Meyers explained once "iff you can take its address its not a rvalue its a lvalue". So isn't the get_scheme_from_url() not simply holding a const ref to temporary? (which most compiler warn about)
>
> ...Or am I missing the point?
>
>> reference to const and implicitly passed to string_view's constructor. The obtained view refers to a dead string.
>>
>>
>> Andrei

September 17, 2014
On Wednesday, 17 September 2014 at 08:57:36 UTC, Szymon Gatner wrote:
> On Wednesday, 17 September 2014 at 08:52:58 UTC, Arjan wrote:
>> On Tuesday, 16 September 2014 at 15:30:49 UTC, Andrei Alexandrescu wrote:
>>> http://www.slideshare.net/yandex/rust-c
>>>
>>> C++ code:
>>>
>>> std::string get_url() {
>>>   return "http://yandex.ru";
>>> }
>>>
>>> string_view get_scheme_from_url(string_view url) {
>>>   unsigned colon = url.find(':');
>>>   return url.substr(0, colon);
>>> }
>>>
>>> int main() {
>>>   auto scheme = get_scheme_from_url(get_url());
>>>   std::cout << scheme << "\n";
>>>   return 0;
>>> }
>>>
>>> string_view has an implicit constructor from const string& (see "basic_string_view(const basic_string<charT, traits, Allocator>& str) noexcept;" in https://isocpp.org/files/papers/N3762.html). The function get_url() returns an rvalue, which in turn gets bound to a
>>
>> Forgive me my ignorance but  get_url() returns a std::string (on the stack), so its address can be token.
>> And iirc the explainer Scott Meyers explained once "iff you can take its address its not a rvalue its a lvalue". So isn't the get_scheme_from_url() not simply holding a const ref to temporary? (which most compiler warn about)
>>
>> ...Or am I missing the point?
>>
>>> reference to const and implicitly passed to string_view's constructor. The obtained view refers to a dead string.
>>>
>>>
>>> Andrei


[ Sorry for double posting, i must have double clicked on "reply" button accidentally. ]

std::string returned from get_url() is a temporary and hence a "rvalue". In fact it's address cannot be taken. It is often helpful to think of lvalues as things that can appear on the left side of assignment expression.
September 17, 2014
BTW. About r-values:

void fun(S s)
{
    fun2(s); //pass s by value.
}

Now, compiler inserts postblit call before func2 call and dtor before end of fun. However, it is able to doesn't it, because after fun2 call, s isn't used.
Thus, we can implement last postblit optimization:
If compiler want to insert postblit, and object is't used after this postblit, compiler is able to doesn't generate postblit and last dtor calls.
Is there limitation of this optimization?
It may 90 percent to solve a r-value ref task.
September 17, 2014
On Tuesday, 16 September 2014 at 15:30:49 UTC, Andrei
Alexandrescu wrote:
> http://www.slideshare.net/yandex/rust-c
>
> C++ code:
>
> std::string get_url() {
>     return "http://yandex.ru";
> }
>
> string_view get_scheme_from_url(string_view url) {
>     unsigned colon = url.find(':');
>     return url.substr(0, colon);
> }
>
> int main() {
>     auto scheme = get_scheme_from_url(get_url());
>     std::cout << scheme << "\n";
>     return 0;
> }
>
> string_view has an implicit constructor from const string& (see "basic_string_view(const basic_string<charT, traits, Allocator>& str) noexcept;" in https://isocpp.org/files/papers/N3762.html). The function get_url() returns an rvalue, which in turn gets bound to a reference to const and implicitly passed to string_view's constructor. The obtained view refers to a dead string.
>
>
> Andrei

I would say the problem with this code is not so much in this
usage example but in the implementation of string_view. Given
string_view aims to be a non-owning reference to a string, it
should prevent such assignments, which is pretty straights
forward to ensure in C++11. Just delete the corresponding
constructor:

    string_view( std::string && ) = delete;

This example, as stated in subsequent answers to your post, is no
different from returning a const reference to a temporary. In
this example, it just happens to be nicely hidden in the
string_view implementation instead of being explicit:

    std::string const & get_url( )
    { return "foo"; }

This has always been a no-no, and C++11 at least now adds the
possibility to refuse such code via deleted functions.

O.
« First   ‹ Prev
1 2