Boost logo

Boost Users :

Subject: Re: [Boost-users] Aliased parameters in boost::bind
From: Evgeny Fraimovitch (evgeny_at_[hidden])
Date: 2017-03-30 06:51:41


> -----Original Message-----
> From: Boost-users [mailto:boost-users-bounces_at_[hidden]] On Behalf
> Of Gavin Lambert via Boost-users
> Sent: Thursday, March 30, 2017 6:09 AM
> To: boost-users_at_[hidden]
> Cc: Gavin Lambert <gavinl_at_[hidden]>
> Subject: Re: [Boost-users] Aliased parameters in boost::bind
>
> On 30/03/2017 11:59, Evgeny Fraimovitch wrote:
> > Although the code is suboptimal, I do not agree it is bad. This lack
> > of pass by reference in this case should not be affecting correctness,
> > since we are not going to modify the object. Also, it might not be
> > std::string. Shared points are often passed by value and they exhibit
> > the same issue. They can be passed as const reference too, as an
> > optimization, however whether this justifies additional clutter in the
> > parameter list is a matter of personal taste.
>
> Perhaps this is a domain-specific concern, but where I work you get
severely
> beaten* if you pass strings *or* shared_ptrs by value. (Or in general
> anything with non-trivial copy semantics.)
>
> (* or at least mocked at the water cooler and code reviews)
I agree it is very important for performance critical code, much less so for
utility code which won't be invoked in tight loops. Anyway, I consider being
mocked at water cooler being better than the compiler silently corrupting
the code on boost version transition.

> Neither memory allocations nor atomic refcount adjustments are free, and
> the function call boundary is rarely the correct place for them to occur.
>
> But yes, ideally this would produce correct output despite being
suboptimal.
> It's one of the unfortunate side effects of perfect forwarding.
>
> Having said that, with the existence of perfect forwarding it means you're
> using C++11, which means you could use lambdas instead of using bind.
They
> don't suffer the same issue because they don't use perfect
> forwarding:
>
> auto triple = [](std::string x) { f(x, x, x); };
> auto triple = [](std::string const& x) { f(x, x, x); };
> auto triple = [](std::string && x) { f(x, x, x); };
>
> Substituting any of these produces correct output; you would only provoke
> the incorrect output if you explicitly std::move(x) in the last version,
which is
> an obvious programmer error.

Using lambdas is exactly how I worked around for it in the place that
originally aliased parameters.

What I am saying is, though, the Boost.Bind tutorial should not promote
parameter aliasing, since it obviously produces extremely fragile code. I
believe forwarding twice anything is a *mistake*, it's just a harmless one,
when the type has trivial move semantics.

>
> It can be most problematic for large codebases with existing pre-C++11
bind
> code, of course, that is later updated to C++11. Hopefully you have unit
tests
> to catch these sorts of things. :)

Well, this instance got caught by a unit test. However, when something as
fundamental as parameter passing is concerned you start wondering how well
you cover that.

>
> There's some other corner cases with bind that are a bit weird, for
example,
> given:
>
> void g(std::string&& x)
> {
> std::string y(std::move(x));
> std::cout << y << std::endl;
> }
>
> (which is even worse than pass-by-value in this case, of course.) This
> compiles and works as expected:
>
> auto fn = boost::bind(g, _1);
> fn(std::string("some string"));
>
> As does this:
>
> std::string x("some string");
> auto fn = boost::bind(g, _1);
> fn(std::move(x));
>
> But this fails to compile:
>
> std::string x("some string");
> auto fn = boost::bind(g, std::move(x));
> fn();
>
> Even though logically they ought to be the same. (The reason this happens
> AFAIK is because fn could be called more than once, but wouldn't be in a
> valid state after the first time; I think someone decided this should be a
> compile error rather than throwing an exception if you do happen to call
it
> twice. It does compile if g doesn't have an rvalue-reference parameter.)
>
> Ironically this is actually one of the scenarios where bind was still
superior to
> lambdas in C++11. Given void g(std::string const& x), you can compile
that
> same last block of code just fine (even with the move), but you couldn't
> write an equivalent lambda (without contortions) since they lacked move-
> capturing. This has been fixed in C++14 though, albeit a bit wordy.
>
>
> _______________________________________________
> Boost-users mailing list
> Boost-users_at_[hidden]
> http://lists.boost.org/mailman/listinfo.cgi/boost-users


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net