Saturday, August 18, 2018

Digest for comp.lang.c++@googlegroups.com - 5 updates in 1 topic

bitrex <user@example.net>: Aug 18 01:43AM -0400

I have the following compiling under gcc 8.2, -std=c++11
 
#include <functional>
#include <iostream>
 
typedef int ContextType;
 
struct Foo {
Foo(std::function<int()>&& bar) : bar_(bar) {}
 
std::function<int()> bar_;
 
int getValue()
{
return bar_();
}
};
 
struct Baz {
template <int (*Ptr)(int)>
static Foo* create(const ContextType& ctx) {
std::function<int()> bar = [&]() { return Ptr(ctx); };
return new Foo(std::move(bar));
}
};
 
int A(int a)
{
return a;
}
 
First I try:
 
int main()
{
auto baz1 = Baz::create<&A>(1);
auto baz2 = Baz::create<&A>(2);
std::cout << baz1->getValue() << std::endl; //prints 2;
std::cout << baz2->getValue() << std::endl; //prints 2;
}
 
And then:
 
int main()
{
auto a = 1;
auto b = 2;
auto baz1 = Baz::create<&A>(a);
auto baz2 = Baz::create<&A>(b);
std::cout << baz1->getValue() << std::endl; //prints 1;
std::cout << baz2->getValue() << std::endl; //prints 2;
}
 
If I change static Foo* create(const ContextType& ctx) {
std::function<int()> bar = [&]() { return Ptr(ctx); };
return new Foo(std::move(bar));
}
 
to take a non-const reference I get a compiler warning about
non-convertibility in the first example. Where can I read in the
standard about how captures work in this situation?
Marcel Mueller <news.5.maazl@spamgourmet.org>: Aug 18 09:27AM +0200

Am 18.08.2018 um 07:43 schrieb bitrex:
> I have the following compiling under gcc 8.2, -std=c++11
[...]
 
> to take a non-const reference I get a compiler warning about
> non-convertibility in the first example. Where can I read in the
> standard about how captures work in this situation?
 
What did you change?
 
Your code works for me without any warning.
 
 
Marcel
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Aug 18 09:57AM +0200

On 18.08.2018 07:43, bitrex wrote:
>       return bar_();
>   }
> };
 
Here `Foo` can only be constructed with a temporary (or rvalue reference
returned by function, more generally an rvalue expression) as argument.
That's IMO a needless restriction. Instead, do this, which accepts both
lvalue and rvalue argument without incurring more copy operations:
 
struct Foo {
Foo(std::function<int()> bar) : bar_(move(bar)) {}
 
std::function<int()> bar_;
 
int value() const
{
return bar_();
}
};
 
 
>     return new Foo(std::move(bar));
>   }
> };
 
This can be simplified to
 
struct Baz {
template <int (*Ptr)(int)>
static Foo* create(const ContextType& ctx) {
auto bar = [&]() { return Ptr(ctx); };
return new Foo(std::move(bar));
}
};
 
 
>     std::cout << baz1->getValue() << std::endl; //prints 2;
>     std::cout << baz2->getValue() << std::endl; //prints 2;
> }
 
That outputs 1 2 for me, not 2 2. But it's not reliable. The temporary
`int`, a.k.a. `ContextType`, that each lambda refers to, has ceased to
formally exist when the output statements are executed, i.e. there's
formally Undefined Behavior due to Dangling References.
 
 
>     std::cout << baz1->getValue() << std::endl; //prints 1;
>     std::cout << baz2->getValue() << std::endl; //prints 2;
> }
 
This one appears to be OK, no UB as far as I can see. :)
 
 
>   }
 
> to take a non-const reference I get a compiler warning about
> non-convertibility in the first example.
 
Yes, you can't bind an rvalue expression such as a numerical literal, to
a reference to non-const.
 
However, since g++ doesn't have a language extension that allows you to
do that you should get an outright error, not a warning.
 
Checking... Yep, you get an error with g++ (version 7.3). And also with
Visual C++ (2017).
 
 
> Where can I read in the
> standard about how captures work in this situation?
 
All you need to know is that capturing by reference gives you a lambda
object with a references to whatever's captured, instead of copies.
 
 
Cheers & hth.,
 
- Alf
Paavo Helde <myfirstname@osa.pri.ee>: Aug 18 11:05AM +0300

On 18.08.2018 8:43, bitrex wrote:
> std::cout << baz1->getValue() << std::endl; //prints 2;
> std::cout << baz2->getValue() << std::endl; //prints 2;
> }
 
Isn't this just an obscure way to create dangling references which has
actually nothing to do with lambdas? I think the relevant standard
clause is 12.2/5:
 
"A temporary object bound to a reference parameter in a function call
(5.2.2) persists until the completion of the full-expression containing
the call."
 
Here, I think this means that the temporary created from 1 persists
until the end of the line:
 
auto baz1 = Baz::create<&A>(1);
 
After that, the lambda is left with a dangling reference inside. Or is
there something else?
bitrex <user@example.net>: Aug 18 12:09PM -0400

On 08/18/2018 03:57 AM, Alf P. Steinbach wrote:
> object with a references to whatever's captured, instead of copies.
 
> Cheers & hth.,
 
> - Alf
 
Great, got it now, thank you. Being able to store lambdas in objects and
construct varying kinds in templated factory methods is very nice
feature to have, particularly when interfacing with legacy code or C
APIs, how did I live without this feature before...
You received this digest because you're subscribed to updates for this group. You can change your settings on the group membership page.
To unsubscribe from this group and stop receiving emails from it send an email to comp.lang.c+++unsubscribe@googlegroups.com.

No comments: