Widget&&&& forward( Widget& param) // instantiation of
{ // std::forward when
return static_cast< Widget&&&&>(param); // T is Widget&&
} // (before reference-
// collapsing)
Applying the reference-collapsing rule that an rvalue reference to an rvalue reference becomes a single rvalue reference, this instantiation emerges:
Widget&&forward(Widget& param) // instantiation of
{ // std::forward when
return static_cast< Widget&&>(param); // T is Widget&&
} // (after reference-
// collapsing)
If you compare this instantiation with the one that results when std::forward
is called with T
set to Widget
, you'll see that they're identical. That means that instantiating std::forward
with an rvalue reference type yields the same result as instantiating it with a non-reference type.
That's wonderful news, because decltype(x)
yields an rvalue reference type when an rvalue is passed as an argument to our lambda's parameter x
. We established above that when an lvalue is passed to our lambda, decltype(x)
yields the customary type to pass to std::forward
, and now we realize that for rvalues, decltype(x)
yields a type to pass to std::forward
that's not conventional, but that nevertheless yields the same outcome as the conventional type. So for both lvalues and rvalues, passing decltype(x)
to std::forward
gives us the result we want. Our perfect-forwarding lambda can therefore be written like this:
auto f =
[](auto&& param) {
return
func(normalize(std::forward< decltype(param)>(param)));
};
From there, it's just a hop, skip, and six dots to a perfect-forwarding lambda that accepts not just a single parameter, but any number of parameters, because C++14 lambdas can also be variadic:
auto f =
[](auto&& ...params) {
return
func(normalize(std::forward(params) ...));
};
Things to Remember
• Use decltype
on auto&&
parameters to std::forward
them.
Item 34: Prefer lambdas to std::bind
.
std::bind
is the C++11 successor to C++98's std::bind1st
and std::bind2nd
, but, informally, it's been part of the Standard Library since 2005. That's when the Standardization Committee adopted a document known as TR1, which included bind
's specification. (In TR1, bind
was in a different namespace, so it was std::tr1::bind
, not std::bind
, and a few interface details were different.) This history means that some programmers have a decade or more of experience using std::bind
. If you're one of them, you may be reluctant to abandon a tool that's served you well. That's understandable, but in this case, change is good, because in C++11, lambdas are almost always a better choice than std::bind
. As of C++14, the case for lambdas isn't just stronger, it's downright ironclad.
This Item assumes that you're familiar with std::bind
. If you're not, you'll want to acquire a basic understanding before continuing. Such an understanding is worthwhile in any case, because you never know when you might encounter uses of std::bind
in a code base you have to read or maintain.
As in Item 32, I refer to the function objects returned from std::bind
as bind objects .
The most important reason to prefer lambdas over std::bind
is that lambdas are more readable. Suppose, for example, we have a function to set up an audible alarm:
// typedef for a point in time (see Item 9 for syntax)
using Time = std::chrono::steady_clock::time_point;
// see Item 10 for "enum class"
enum class Sound { Beep, Siren, Whistle };
// typedef for a length of time
using Duration = std::chrono::steady_clock::duration;
// at time t, make sound s for duration d
void setAlarm(Time t, Sound s, Duration d);
Further suppose that at some point in the program, we've determined we'll want an alarm that will go off an hour after it's set and that will stay on for 30 seconds. The alarm sound, however, remains undecided. We can write a lambda that revises setAlarm
's interface so that only a sound needs to be specified:
// setSoundL ("L" for "lambda") is a function object allowing a
// sound to be specified for a 30-sec alarm to go off an hour
// after it's set
auto setSoundL =
[](Sound s) {
// make std::chrono components available w/o qualification
using namespace std::chrono;
setAlarm(steady_clock::now() + hours(1),// alarm to go off
s, // in an hour for
seconds(30)); // 30 seconds
};
I've highlighted the call to setAlarm
inside the lambda. This is a normal-looking function call, and even a reader with little lambda experience can see that the parameter s passed to the lambda is passed as an argument to setAlarm
.
We can streamline this code in C++14 by availing ourselves of the standard suffixes for seconds ( s
), milliseconds ( ms
), hours ( h
), etc., that build on C++11's support for user-defined literals. These suffixes are implemented in the std::literals
namespace, so the above code can be rewritten as follows:
auto setSoundL =
[](Sound s) {
using namespace std::chrono;
using namespace std::literals; // for C++14 suffixes
setAlarm(steady_clock::now() + 1h, // C++14, but
s, // same meaning
30s); // as above
};
Our first attempt to write the corresponding std::bind
call is below. It has an error that we'll fix in a moment, but the correct code is more complicated, and even this simplified version brings out some important issues:
using namespace std::chrono; // as above
using namespace std::literals;
using namespace std::placeholders; // needed for use of "_1"
auto setSoundB = // "B" for "bind"
std::bind(setAlarm,
steady_clock::now() + 1h, // incorrect! see below
_1,
30s);
I'd like to highlight the call to setAlarm
here as I did in the lambda, but there's no call to highlight. Readers of this code simply have to know that calling setSoundB
invokes setAlarm
with the time and duration specified in the call to std::bind
. To the uninitiated, the placeholder “ _1
” is essentially magic, but even readers in the know have to mentally map from the number in that placeholder to its position in the std::bind
parameter list in order to understand that the first argument in a call to setSoundB
is passed as the second argument to setAlarm
. The type of this argument is not identified in the call to std::bind
, so readers have to consult the setAlarm
declaration to determine what kind of argument to pass to setSoundB
.
Читать дальше