Effective Modern C++

【Effective Modern C++(6)】lambda表

2018-12-21  本文已影响22人  downdemo
#include <functional>
#include <iostream>
 
struct Foo {
    Foo(int num) : num_(num) {}
    void print_add(int i) const { std::cout << num_+i << '\n'; }
    int num_;
};
 
void print_num(int i)
{
    std::cout << i << '\n';
}
 
struct PrintNum {
    void operator()(int i) const
    {
        std::cout << i << '\n';
    }
};
 
int main()
{
    // store a free function
    std::function<void(int)> f_display = print_num;
    f_display(-9);
 
    // store a lambda
    std::function<void()> f_display_42 = []() { print_num(42); };
    f_display_42();
 
    // store the result of a call to std::bind
    std::function<void()> f_display_31337 = std::bind(print_num, 31337);
    f_display_31337();
 
    // store a call to a member function
    std::function<void(const Foo&, int)> f_add_display = &Foo::print_add;
    const Foo foo(314159);
    f_add_display(foo, 1);
    f_add_display(314159, 1);
 
    // store a call to a data member accessor
    std::function<int(Foo const&)> f_num = &Foo::num_;
    std::cout << "num_: " << f_num(foo) << '\n';
 
    // store a call to a member function and object
    using std::placeholders::_1;
    std::function<void(int)> f_add_display2 = std::bind( &Foo::print_add, foo, _1 );
    f_add_display2(2);
 
    // store a call to a member function and object ptr
    std::function<void(int)> f_add_display3 = std::bind( &Foo::print_add, &foo, _1 );
    f_add_display3(3);
 
    // store a call to a function object
    std::function<void(int)> f_display_obj = PrintNum();
    f_display_obj(18);
}

// output
-9
42
31337
314160
314160
num_: 314159
314161
314162
18
#include <random>
#include <iostream>
#include <memory>
#include <functional>
 
void f(int n1, int n2, int n3, const int& n4, int n5)
{
    std::cout << n1 << ' ' << n2 << ' ' << n3 << ' ' << n4 << ' ' << n5 << '\n';
}
 
int g(int n1)
{
    return n1;
}
 
struct Foo {
    void print_sum(int n1, int n2)
    {
        std::cout << n1+n2 << '\n';
    }
    int data = 10;
};
 
int main()
{
    using namespace std::placeholders;  // for _1, _2, _3...
 
    // demonstrates argument reordering and pass-by-reference
    int n = 7;
    // (_1 and _2 are from std::placeholders, and represent future
    // arguments that will be passed to f1)
    auto f1 = std::bind(f, _2, _1, 42, std::cref(n), n);
    n = 10;
    f1(1, 2, 1001); // 1 is bound by _1, 2 is bound by _2, 1001 is unused
                    // makes a call to f(2, 1, 42, n, 7)
 
    // nested bind subexpressions share the placeholders
    auto f2 = std::bind(f, _3, std::bind(g, _3), _3, 4, 5);
    f2(10, 11, 12); // makes a call to f(12, g(12), 12, 4, 5);
 
    // common use case: binding a RNG with a distribution
    std::default_random_engine e;
    std::uniform_int_distribution<> d(0, 10);
    auto rnd = std::bind(d, e); // a copy of e is stored in rnd
    for(int n=0; n<10; ++n)
        std::cout << rnd() << ' ';
    std::cout << '\n';
 
    // bind to a pointer to member function
    Foo foo;
    auto f3 = std::bind(&Foo::print_sum, &foo, 95, _1);
    f3(5);
 
    // bind to a pointer to data member
    auto f4 = std::bind(&Foo::data, _1);
    std::cout << f4(foo) << '\n';
 
    // smart pointers can be used to call members of the referenced objects, too
    std::cout << f4(std::make_shared<Foo>(foo)) << '\n'
              << f4(std::make_unique<Foo>(foo)) << '\n';
}

// output
2 1 42 10 7
12 12 12 4 5
1 5 0 2 0 8 2 2 10 8 // 这里是随机数,结果可能不同
100
10
10
10

31 避免默认捕获模式

using FilterContainer = std::vector<std::function<bool(int)>>;
FilterContainer filters;

void addDivisorFilter()
{
    auto calc1 = computeSomeValue1();
    auto calc2 = computeSomeValue2();
    auto divisor = computeDivisor(calc1, calc2);
    filters.emplace_back( // 危险:对divisor的引用可能空悬
        [&](int value) { return value % divisor == 0; }
    );
}
filters.emplace_back( // 现在divisor不会空悬
    [=](int value) { return value % divisor == 0; }
);
class Widget {
public:
    … // ctors, etc.
    void addFilter() const; // add an entry to filters
private:
    int divisor; // used in Widget's filter
};

void Widget::addFilter() const
{
    filters.emplace_back(
        [=](int value) { return value % divisor == 0; }
    );
}
void Widget::addFilter() const
{
    filters.emplace_back(
        [](int value) { return value % divisor == 0; } // 错误:没有可捕获的divisor
    );
}
void Widget::addFilter() const
{
    filters.emplace_back(
        [divisor](int value) { return value % divisor == 0; } // 错误:没有可捕获的divisor
    );
}
void Widget::addFilter() const
{
    auto currentObjectPtr = this;
    filters.emplace_back(
    [currentObjectPtr](int value)
        { return value % currentObjectPtr->divisor == 0; }
    );
}
using FilterContainer = std::vector<std::function<bool(int)>>;
FilterContainer filters;

void doSomeWork()
{
    auto pw = std::make_unique<Widget>();
    pw->addFilter();
    …
} // 销毁Widget:filters现在持有空悬指针!
void Widget::addFilter() const
{
    auto divisorCopy = divisor; // 拷贝数据成员
    filters.emplace_back(
    [divisorCopy](int value) // 捕获拷贝对象
        { return value % divisorCopy == 0; } // 使用拷贝对象
    );
}
void Widget::addFilter() const
{
    filters.emplace_back(
    [divisor = divisor](int value) // C++14:将divisor拷贝进闭包
        { return value % divisor == 0; } // 使用拷贝对象
    );
}
void addDivisorFilter()
{
    static auto calc1 = computeSomeValue1();
    static auto calc2 = computeSomeValue2();
    static auto divisor = computeDivisor(calc1, calc2);
    filters.emplace_back(
        [=](int value) // 未捕获任何东西
        { return value % divisor == 0; } // 指向static对象
    );
    ++divisor; // 意外修改divisor将导致每个lambda的行为不同
}

32 使用初始化捕获将对象移入闭包

class Widget { // some useful type
public:
    …
    bool isValidated() const;
    bool isProcessed() const;
    bool isArchived() const;
private:
    …
};

auto pw = std::make_unique<Widget>();
…
auto func =
    [pw = std::move(pw)] // 用std::move(pw)初始化闭包类的成员变量pw
    { return pw->isValidated() && pw->isArchived(); };
auto func =
    [pw = std::make_unique<Widget>()]
    { return pw->isValidated() && pw->isArchived(); };
class IsValAndArch {
public:
    using DataType = std::unique_ptr<Widget>;
    explicit IsValAndArch(DataType&& ptr)
    : pw(std::move(ptr)) {}
    bool operator()() const
    { return pw->isValidated() && pw->isArchived(); }
private:
    DataType pw;
};

auto func = IsValAndArch(std::make_unique<Widget>());
auto func = std::bind(
    [](const std::unique_ptr<Widget>& pw)
    { return pw->isValidated() && pw->isArchived(); },
    std::make_unique<Widget>()
);
std::vector<double> data; // 要移动到闭包的对象
...
auto f = [data = std::move(data)] { ... }; // C++14:初始化捕获
auto f2 = std::bind( // C++11:模拟初始化捕获
    [] (const std::vector<double>& data) { ... }, std::move(data)
);
auto f3 =
    std::bind( // C++11对可变lambda模拟初始化捕获
        [](std::vector<double>& data) mutable
        { ... },
        std::move(data)
);

33 对auto&&类型形参使用decltype来std::forward

auto f = [](auto x){ return func(normalize(x)); };
// the closure class’s function call operator looks like this:
class SomeCompilerGeneratedClassName {
public:
    template<typename T>
    auto operator()(T x) const
    { return func(normalize(x)); }
    … // 闭包类的其他功能
};
auto f = [](auto&& x)
    { return func(normalize(std::forward<???>(x))); };
auto f = [](auto&& x)
    { return func(normalize(std::forward<decltype(x)>(x))); };
auto f = [](auto&&... args)
    { return func(normalize(std::forward<decltype(args)>(args)...)); };

34 使用lambda替代std::bind

概述

class PolyWidget {
public:
    template<typename T>
    void operator()(const T& param);
    …
};
PolyWidget pw;
auto boundPW = std::bind(pw, _1);
boundPW(1930); // pass int to PolyWidget::operator()
boundPW(nullptr); // pass nullptr to PolyWidget::operator()
boundPW("Rosebud"); // pass string literal to PolyWidget::operator()
auto boundPW = [pw](const auto& x) { pw(x); };

详述

using Time = std::chrono::steady_clock::time_point;
enum class Sound { Beep, Siren, Whistle };
using Duration = std::chrono::steady_clock::duration;
void setAlarm(Time t, Sound s, Duration d); // 设置声音警报的函数

auto setSoundL = [](Sound s)
{
    using namespace std::chrono;
    setAlarm(steady_clock::now() + hours(1), s, seconds(30));
};
auto setSoundL = [](Sound s)
{
    using namespace std::chrono;
    using namespace std::literals; // for C++14 suffixes
    setAlarm(steady_clock::now() + 1h, s, 30s);
};
using namespace std::chrono;
using namespace std::literals;
using namespace std::placeholders; // needed for use of "_1"
auto setSoundB = std::bind(
    setAlarm,
    steady_clock::now() + 1h, // 不符合目的的做法
    _1,
    30s);
auto setSoundB = std::bind(
    setAlarm,
    std::bind(std::plus<>(), steady_clock::now(), 1h), // C++14
    _1,
    30s);
using namespace std::chrono;
using namespace std::placeholders;

auto setSoundB = std::bind(
    setAlarm,
    std::bind(std::plus<steady_clock::time_point>(),
        steady_clock::now(),
        hours(1)),
    _1,
    seconds(30));
auto setSoundL = [](Sound s)
{
    using namespace std::chrono;
    using namespace std::literals; // for C++14 suffixes
    setAlarm(steady_clock::now() + 1h, s, 30s);
};
enum class Volume { Normal, Loud, LoudPlusPlus };
void setAlarm(Time t, Sound s, Duration d, Volume v);
using SetAlarm3ParamType = void(*)(Time t, Sound s, Duration d);
auto setSoundB = std::bind(
    static_cast<SetAlarm3ParamType>(setAlarm), // OK
    std::bind(std::plus<>(), steady_clock::now(), 1h),
    _1,
    30s);
setSoundL(Sound::Siren); // body of setAlarm may well be inlined here
setSoundB(Sound::Siren); // body of setAlarm is less likely to be inlined here
// C++14
auto betweenL =
    [lowVal, highVal] (const auto& val)
    { return lowVal <= val && val <= highVal; };
// C++11
auto betweenL =
    [lowVal, highVal] (int val)
    { return lowVal <= val && val <= highVal; };
// C++14
using namespace std::placeholders;
auto betweenB = std::bind(
    std::logical_and<>(),
    std::bind(std::less_equal<>(), lowVal, _1),
    std::bind(std::less_equal<>(), _1, highVal));
// C++11
auto betweenB = std::bind(
    std::logical_and<bool>(),
    std::bind(std::less_equal<int>(), lowVal, _1),
    std::bind(std::less_equal<int>(), _1, highVal));
enum class CompLevel { Low, Normal, High }; // 压缩等级
Widget compress(const Widget& w, CompLevel lev);

Widget w;
using namespace std::placeholders;
auto compressRateB = std::bind(compress, w, _1);
auto compressRateB = std::bind(compress, std::ref(w), _1);
auto compressRateL =
    [w](CompLevel lev) // w按值捕获,lev按值传递
    { return compress(w, lev); };

compressRateL(CompLevel::High); // 实参按值传递
compressRateB(CompLevel::High); // 实参如何传递?
上一篇 下一篇

猜你喜欢

热点阅读