Example Code: Modern C++#

Manipulate data using a raw pointer (01_copy.cpp).#
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <memory>

class IsCopied
{

public:

    static IsCopied & instance()
    {
        // This is a singleton.
        static IsCopied inst;
        return inst;
    }

    IsCopied & on() { m_status = true; return *this; }

    operator bool() const { return m_status; }

    ~IsCopied() = default;

private:

    IsCopied() : m_status(false) {}

    IsCopied(IsCopied const & ) = delete;
    IsCopied(IsCopied       &&) = delete;
    IsCopied & operator=(IsCopied const & ) = delete;
    IsCopied & operator=(IsCopied       &&) = delete;

    bool m_status;

}; /* end class IsCopied */

class Data
{

public:

    constexpr const static size_t NELEM = 1024*8;

    Data()
    {
        std::cout << "Data constructed @" << this << std::endl;
    }

    Data(Data const & other)
    {
        copy_from(other);
        std::cout << "Data copied to @" << this << " from @" << &other << std::endl;
    }

    Data & operator=(Data const & other)
    {
        copy_from(other);
        std::cout << "Data copy assigned to @" << this << " from @" << &other << std::endl;
        return *this;
    }

    ~Data()
    {
        std::cout << "Data destructed @" << this << std::endl;
    }

    size_t size() const { return NELEM; }
    int   operator[](size_t it) const { return m_buffer[it]; }
    int & operator[](size_t it)       { return m_buffer[it]; }

    bool is_manipulated() const
    {
        for (size_t it=0; it < size(); ++it)
        {
            if ((*this)[it] != it) { return false; }
        }
        return true;
    }

private:

    void copy_from(Data const & other)
    {
        for (size_t it=0; it < NELEM; ++it)
        {
            m_buffer[it] = other.m_buffer[it];
        }
        // Mark copied.
        IsCopied::instance().on();
    }

    // A lot of data that we don't want to reconstruct.
    int m_buffer[NELEM];

}; /* end class Data */

void manipulate_with_reference(Data & data, int value)
{
    std::cout << "Manipulate with reference: " << &data << std::endl;

    for (size_t it=0; it < data.size(); ++it)
    {
        data[it] = value + it;
    }
    // In a real consumer function we will do much more meaningful operations.

    // However, we cannot destruct an object passed in with a reference.
}

Data worker1()
{
    Data data;

    // Manipulate the Data object.
    manipulate_with_reference(data, 3);

    return data;
}

Data worker2()
{
    Data data = worker1();

    // Manipulate the Data object, again.
    manipulate_with_reference(data, 8);

    return data;
}

int main(int argc, char ** argv)
{
    std::cout
        << (bool(IsCopied::instance()) ? "Something" : "Nothing")
        << " is copied" << std::endl;
    Data data = worker2();
    std::cout
        << (bool(IsCopied::instance()) ? "Something" : "Nothing")
        << " is copied" << std::endl;
    return 0;
}
$ g++ 01_copy.cpp -o 01_copy -std=c++17 -g -O3
$ g++ 01_copy.cpp -o 01_copy -std=c++17 -g -O0
The interaction between movement and copy elision (02_move.cpp).#
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <memory>

class Status
{

public:

    static Status & instance()
    {
        // This is a singleton.
        static Status inst;
        return inst;
    }

    bool is_copied() const { return m_copied; }
    bool is_moved() const { return m_moved; }

    void set_copied() { m_copied = true; }
    void set_moved() { m_moved = true; }

private:

    Status() : m_copied(false), m_moved(false) {}

    bool m_copied;
    bool m_moved;

}; /* end class Status */

class Data
{

public:

    constexpr const static size_t NELEM = 1024*8;

    Data()
    {
        m_buffer = new int[NELEM];
        std::cout << "Data constructed @" << this
                  << std::endl;
    }

    Data(Data const & other)
    {
        m_buffer = new int[NELEM];
        copy_from(other);
        std::cout << "Data copied to @" << this
                  << " from @" << &other << std::endl;
    }

    Data & operator=(Data const & other)
    {
        if (nullptr == m_buffer) { m_buffer = new int[NELEM]; }
        copy_from(other);
        std::cout << "Data copy assigned to @" << this
                  << " from @" << &other << std::endl;
        return *this;
    }

    Data(Data && other)
    {
        m_buffer = other.m_buffer;
        other.m_buffer = nullptr;
        std::cout << "Data moved to @" << this
                  << " from @" << &other << std::endl;
        Status::instance().set_moved();
    }

    Data & operator=(Data && other)
    {
        if (m_buffer) { delete[] m_buffer; }
        m_buffer = other.m_buffer;
        other.m_buffer = nullptr;
        std::cout << "Data move assigned to @" << this
                  << " from @" << &other << std::endl;
        Status::instance().set_moved();
        return *this;
    }

    ~Data()
    {
        if (m_buffer) { delete[] m_buffer; }
        std::cout << "Data destructed @" << this << std::endl;
    }

    size_t size() const { return NELEM; }
    int   operator[](size_t it) const { return m_buffer[it]; }
    int & operator[](size_t it)       { return m_buffer[it]; }

    bool is_manipulated() const
    {
        for (size_t it=0; it < size(); ++it)
        {
            if ((*this)[it] != it) { return false; }
        }
        return true;
    }

private:

    void reset()
    {
    }

    void copy_from(Data const & other)
    {
        for (size_t it=0; it < NELEM; ++it)
        {
            m_buffer[it] = other.m_buffer[it];
        }
        Status::instance().set_copied();
    }

    // A lot of data that we don't want to reconstruct.
    int * m_buffer;

}; /* end class Data */

void manipulate_with_reference(Data & data, int value)
{
    std::cout << "Manipulate with reference: " << &data << std::endl;

    for (size_t it=0; it < data.size(); ++it)
    {
        data[it] = value + it;
    }
    // In a real consumer function we will do much more meaningful operations.

    // However, we cannot destruct an object passed in with a reference.
}

Data worker1()
{
    Data data;

    // Manipulate the Data object.
    manipulate_with_reference(data, 3);

    return data;
}

Data worker2()
{
    Data data = worker1();

    // Manipulate the Data object, again.
    manipulate_with_reference(data, 8);

#ifdef FORCEMOVE
    // Explicit move semantics destroys copy elision.
    return std::move(data);
#else
    return data;
#endif
}

int main(int argc, char ** argv)
{
    std::cout
        << "Status:"
        << (bool(Status::instance().is_copied()) ? " copied" : " uncopied")
        << (bool(Status::instance().is_moved()) ? " moved" : " unmoved")
        << std::endl;
    Data data = worker2();
    std::cout
        << "Status:"
        << (bool(Status::instance().is_copied()) ? " copied" : " uncopied")
        << (bool(Status::instance().is_moved()) ? " moved" : " unmoved")
        << std::endl;

    return 0;
}
$ g++ 02_move.cpp -o 02_move -std=c++17 -g -O3
$ g++ 02_move.cpp -o 02_move -std=c++17 -g -O3 -DFORCEMOVE
Concatenate containers (03_accumulate.cpp).#
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <memory>
#include <iterator>
#include <vector>

class Data
{

public:

    constexpr const static size_t NELEM = 1024*8;

    Data(size_t serial)
      : m_serial(serial)
    {
        m_buffer = new int[NELEM];
        initialize();
        std::cout << "Data #" << m_serial << " constructed @" << this
                  << std::endl;
    }

    Data(Data const & other)
    {
        m_serial = other.m_serial;
        m_buffer = new int[NELEM];
        copy_from(other);
        std::cout << "Data #" << m_serial << " copied to @" << this
                  << " from @" << &other << std::endl;
    }

    Data & operator=(Data const & other)
    {
        m_serial = other.m_serial;
        if (nullptr == m_buffer) { m_buffer = new int[NELEM]; }
        copy_from(other);
        std::cout << "Data #" << m_serial << " copy assigned to @" << this
                  << " from @" << &other << std::endl;
        return *this;
    }

#ifdef MOVENOEXCEPT
    Data(Data && other) noexcept
#else // MOVENOEXCEPT
    Data(Data && other)
#endif // MOVENOEXCEPT
    {
        m_serial = other.m_serial;
        m_buffer = other.m_buffer;
        other.m_buffer = nullptr;
        std::cout << "Data #" << m_serial << " moved to @" << this
                  << " from @" << &other << std::endl;
    }

    Data & operator=(Data && other)
    {
        m_serial = other.m_serial;
        if (m_buffer) { delete[] m_buffer; }
        m_buffer = other.m_buffer;
        other.m_buffer = nullptr;
        std::cout << "Data #" << m_serial << " move assigned to @" << this
                  << " from @" << &other << std::endl;
        return *this;
    }

    ~Data()
    {
        if (m_buffer) { delete[] m_buffer; }
        std::cout << "Data #" << m_serial << " destructed @" << this
                  << std::endl;
    }

    size_t size() const { return NELEM; }
    int   operator[](size_t it) const { return m_buffer[it]; }
    int & operator[](size_t it)       { return m_buffer[it]; }

    bool is_initialized() const
    {
        for (size_t it=0; it < size(); ++it)
        {
            if ((*this)[it] != it) { return false; }
        }
        return true;
    }

private:

    void initialize()
    {
        for (size_t it=0; it < size(); ++it)
        {
            (*this)[it] = it;
        }
    }

    void copy_from(Data const & other)
    {
        for (size_t it=0; it < NELEM; ++it)
        {
            m_buffer[it] = other.m_buffer[it];
        }
    }

    size_t m_serial;

    // A lot of data that we don't want to reconstruct.
    int * m_buffer;

}; /* end class Data */

std::vector<Data> inner1(size_t start, size_t len)
{
    std::cout << "** inner1 begins with " << start << std::endl;
    std::vector<Data> ret;
    for (size_t it=0; it < len; ++it)
    {
        Data data(start+it);
        ret.emplace_back(std::move(data));
    }
    return ret;
}

void outer1(size_t len)
{
    std::cout << "* outer1 begins" << std::endl;
    std::vector<Data> vec;
    for (size_t it=0; it < len; ++it)
    {
        std::cout << std::endl;
        std::cout << "* outer1 loop it=" << it << " begins" << std::endl;
        std::vector<Data> subvec = inner1(vec.size(), it+1);
        std::cout << "* outer1 obtained inner1 at " << vec.size() << std::endl;
        vec.insert(
            vec.end()
          , std::make_move_iterator(subvec.begin())
          , std::make_move_iterator(subvec.end())
        );
        std::cout << "* outer1 inserted subvec.size()=" << subvec.size() << std::endl;
    }
    std::cout << "* outer1 result.size() = " << vec.size() << std::endl << std::endl;
}

void inner2(size_t start, size_t len, std::vector<Data> & result /* for output */)
{
    std::cout << "** inner2 begins with " << start << std::endl;
    for (size_t it=0; it < len; ++it)
    {
        Data data(start+it);
        result.emplace_back(std::move(data));
    }
}

void outer2(size_t len)
{
    std::cout << "* outer2 begins" << std::endl;
    std::vector<Data> vec;
    for (size_t it=0; it < len; ++it)
    {
        std::cout << std::endl;
        std::cout << "* outer2 loop it=" << it << " begins" << std::endl;
        inner2(vec.size(), it+1, vec);
    }
    std::cout << "* outer2 result.size() = " << vec.size() << std::endl << std::endl;
}

struct Accumulator
{

public:
    // This can be called if consumers want the sub-operation one by one, and
    // make the code more testable. But it isn't really used in the example.
    std::vector<Data> inner1(size_t start, size_t len)
    {
        std::cout << "** Accumulator::inner1 begins with " << start << std::endl;
        std::vector<Data> ret;
        ret.reserve(len);
        inner2(start, len, ret);
        return ret;
    }

private:
    // Caller does not see this private helper that takes an output argument.
    void inner2(size_t start, size_t len, std::vector<Data> & ret)
    {
        std::cout << "** Accumulator::inner2 begins with " << start << std::endl;
        for (size_t it=0; it < len; ++it)
        {
            Data data(start+it);
            ret.emplace_back(std::move(data));
        }
    }

public:
    // This is used when batch operation is in demand.
    void outer(size_t len)
    {
        std::cout << "* Accumulator::outer begins" << std::endl;
        result.reserve(len*(len+1)/2);
        for (size_t it=0; it < len; ++it)
        {
            std::cout << std::endl;
            std::cout << "* Accumulator::outer loop it=" << it
                      << " begins" << std::endl;
            // The output argument passed into the private helper is a private
            // member datum.
            inner2(result.size(), it+1, result);
        }
        std::cout << "* Accumulator::outer result.size() = "
                  << result.size() << std::endl << std::endl;
    }

public:
    std::vector<Data> result;

}; /* end struct Accumulator */

int main(int argc, char ** argv)
{
#ifndef OTYPE
#define OTYPE 1
#endif

#if OTYPE == 1
    outer1(3);
#elif OTYPE == 2
    outer2(3);
#elif OTYPE == 3
    Accumulator().outer(3);
#endif
}
$ g++ 03_accumulate.cpp -o 03_accumulate -std=c++17 -g -O3 -DOTYPE=1
$ g++ 03_accumulate.cpp -o 03_accumulate -std=c++17 -g -O3 -DMOVENOEXCEPT -DOTYPE=1
$ g++ 03_accumulate.cpp -o 03_accumulate -std=c++17 -g -O3 -DMOVENOEXCEPT -DOTYPE=2
$ g++ 03_accumulate.cpp -o 03_accumulate -std=c++17 -g -O3 -DMOVENOEXCEPT -DOTYPE=3
Variadic template for factory function (01_factory.cpp).#
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
#include <type_traits>
#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <memory>
#include <iterator>
#include <vector>

class Data
  : std::enable_shared_from_this<Data>
{

private:

    class ctor_passkey {};

public:

    constexpr const static size_t NELEM = 1024*8;

    /* 'create' only expose some of the constructors */
    static std::shared_ptr<Data> create(size_t serial)
    {
        return std::make_shared<Data>(serial, ctor_passkey());
    }

    /* 'make' unconditionally forward to every constructor */
    template < typename ... Args >
    static std::shared_ptr<Data> make(Args && ... args)
    {
        return std::make_shared<Data>(std::forward<Args>(args) ..., ctor_passkey());
    }

    Data(size_t serial, ctor_passkey const &)
      : m_serial(serial)
    {
        m_buffer = new int[NELEM];
        initialize(0);
        std::cout << "Data #" << m_serial << " constructed @" << this
                  << "(serial=" << m_serial << ")" << std::endl;
    }

    Data(size_t serial, int base, ctor_passkey const &)
      : m_serial(serial+base)
    {
        m_buffer = new int[NELEM];
        initialize(0);
        std::cout << "Data #" << m_serial << " constructed @" << this
                  << "(serial=" << m_serial << ")"
                  << "(base=" << base << ")" << std::endl;
    }

    // Proxy to copy and move constructor.
    Data(Data const &  other, ctor_passkey const &)
      : Data(std::forward<Data const &>(other)) {}
    Data(Data       && other, ctor_passkey const &)
      : Data(std::forward<Data &&>(other)) {}

    Data(Data const & other)
    {
        m_serial = other.m_serial;
        m_buffer = new int[NELEM];
        copy_from(other);
        std::cout << "Data #" << m_serial << " copied to @" << this
                  << " from @" << &other << std::endl;
    }

    Data(Data && other) noexcept
    {
        m_serial = other.m_serial;
        m_buffer = other.m_buffer;
        other.m_buffer = nullptr;
        std::cout << "Data #" << m_serial << " moved to @" << this
                  << " from @" << &other << std::endl;
    }

    // Turn off default constructor.
    Data() = delete;

    // Turn off assignment operators before we know how they should behave.
    Data & operator=(Data const & ) = delete;
    Data & operator=(Data       &&) = delete;

    ~Data()
    {
        if (m_buffer) { delete[] m_buffer; }
        std::cout << "Data #" << m_serial << " destructed @" << this << std::endl;
    }

    size_t size() const { return NELEM; }
    int   operator[](size_t it) const { return m_buffer[it]; }
    int & operator[](size_t it)       { return m_buffer[it]; }

    bool is_initialized() const
    {
        for (size_t it=0; it < size(); ++it)
        {
            if ((*this)[it] != it) { return false; }
        }
        return true;
    }

private:

    void initialize(int base)
    {
        for (size_t it=0; it < size(); ++it)
        {
            (*this)[it] = base + it;
        }
    }

    void copy_from(Data const & other)
    {
        for (size_t it=0; it < NELEM; ++it)
        {
            m_buffer[it] = other.m_buffer[it];
        }
    }

    size_t m_serial;

    // A lot of data that we don't want to reconstruct.
    int * m_buffer;

}; /* end class Data */

std::vector<std::shared_ptr<Data>> inner1(size_t base, size_t len)
{
    std::cout << "** inner1 begins with " << base << std::endl;
    std::vector<std::shared_ptr<Data>> ret;
    for (size_t it=0; it < len; ++it)
    {
        std::shared_ptr<Data> data;
        if (0 == base)
        {
#ifdef USE_CREATE
            data = Data::create(it);
#else
            data = Data::make(it);
#endif
        }
        else
        {
#ifdef USE_CREATE
            data = Data::create(it, base);
#else
            data = Data::make(it, base);
#endif
        }
        ret.emplace_back(data);
    }
    return ret;
}

void outer1(size_t len)
{
    std::cout << "* outer1 begins" << std::endl;
    std::vector<std::shared_ptr<Data>> vec;
    for (size_t it=0; it < len; ++it)
    {
        std::cout << std::endl;
        std::cout << "* outer1 loop it=" << it << " begins" << std::endl;
        std::vector<std::shared_ptr<Data>> subvec = inner1(vec.size(), it+1);
        std::cout << "* outer1 obtained inner1 at " << vec.size() << std::endl;
        vec.insert(
            vec.end()
          , std::make_move_iterator(subvec.begin())
          , std::make_move_iterator(subvec.end())
        );
        std::cout << "* outer1 inserted subvec.size()=" << subvec.size() << std::endl;
    }
    std::cout << "* outer1 result.size() = " << vec.size() << std::endl << std::endl;

#ifdef SHOW_PERFECT_FORWARD
    vec.emplace_back(Data::make(*vec[0]));
    vec.emplace_back(Data::make(std::move(*vec[1])));
#endif

    std::cout << "* outer1 end" << std::endl << std::endl;
}

int main(int argc, char ** argv)
{
    outer1(3);
    return 0;
}
$ g++ 01_factory.cpp -o 01_factory -std=c++17 -g -O3 -DUSE_CREATE
$ g++ 01_factory.cpp -o 01_factory -std=c++17 -g -O3
$ g++ 01_factory.cpp -o 01_factory -std=c++17 -g -O3 -DSHOW_PERFECT_FORWARD
C++ lambda (01_lambda.cpp).#
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>
#include <algorithm>
#include <vector>

struct Functor
{
    bool operator()(int v)
    {
        return 0 == v % 23;
    }
}; /* end struct Functor */

int main(int argc, char ** argv)
{
    std::vector<int> data(63712);
    for (size_t i=0 ; i<data.size(); ++i) { data[i] = i;}

    std::cout
        << "Number divisible by 23 (count by functor): "
        << std::count_if(data.begin(), data.end(), Functor())
        << std::endl;

    std::cout
        << "Number divisible by 23 (count by lambda): "
        << std::count_if(data.begin(), data.end(), [](int v){ return 0 == v%23; })
        << std::endl;

    // Demonstrate the similarity between a functor and a lambda.
    auto le = [](int v){ return 0 == v%23; };
    Functor func;
    static_assert(sizeof(le) == sizeof(func));
    static_assert(1 == sizeof(le));
    static_assert(1 == sizeof(func));

    return 0;
}
$ g++ 01_lambda.cpp -o 01_lambda -std=c++17 -g -O3
Store a lambda in a variable (02_stored.cpp).#
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <iostream>
#include <algorithm>
#include <vector>
#include <functional>

int main(int argc, char ** argv)
{
    std::vector<int> data(63712);
    for (size_t i=0 ; i<data.size(); ++i) { data[i] = i;}

    std::cout
        << "Number divisible by 23 (count by lambda inline): "
        << std::count_if(data.begin(), data.end(), [](int v){ return 0 == v%23; })
        << std::endl;

    auto condition = [](int v){ return 0 == v%23; };

    std::cout
        << "Number divisible by 23 (count by lambda in auto): "
        << std::count_if(data.begin(), data.end(), condition)
        << std::endl;

    std::function<bool (int)> condition_function = [](int v){ return 0 == v%23; };

    std::cout
        << "Number divisible by 23 (count by lambda in std::function): "
        << std::count_if(data.begin(), data.end(), condition_function)
        << std::endl;

#ifdef SHOW_DIFF
    // Difference between lambda and std::function.
    std::cout
        << std::endl
        << "The differences between lambda and std::function"
        << std::endl;
    std::cout
        << "type name of lambda: "
        << typeid(condition).name() << std::endl;
    std::cout
        << "type name of std::function: "
        << typeid(condition_function).name() << std::endl;

    std::cout
        << "size of lambda: "
        << sizeof(condition) << std::endl;
    std::cout
        << "size of std::function: "
        << sizeof(condition_function) << std::endl;
#endif

    return 0;
}
$ g++ 02_stored.cpp -o 02_stored -std=c++17 -g -O3
$ g++ 02_stored.cpp -o 02_stored -std=c++17 -g -O3 -DSHOW_DIFF
Store a lambda in a variable, demangled version (02_stored_demangle.cpp).#
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <iostream>
#include <algorithm>
#include <vector>
#include <memory>

// NOTE: This isn't guaranteed to work in every compiler.
#include <cxxabi.h>

std::string demangle(const char* name)
{
    // An arbitrary value to eliminate the compiler warning.
    int status = -1;

    std::unique_ptr<char, void(*)(void*)> res(
        abi::__cxa_demangle(name, NULL, NULL, &status),
        std::free
    );

    return (status==0) ? res.get() : name;
}

int main(int argc, char ** argv)
{
    std::vector<int> data(63712);
    for (size_t i=0 ; i<data.size(); ++i) { data[i] = i;}

    std::cout
        << "Number divisible by 23 (count by inline lambda): "
        << std::count_if(data.begin(), data.end(), [](int v){ return 0 == v%23; })
        << std::endl;

    auto condition = [](int v){ return 0 == v%23; };

    std::cout
        << "Number divisible by 23 (count by stored lambda): "
        << std::count_if(data.begin(), data.end(), condition)
        << std::endl;

    std::cout << "type name of condition: " << demangle(typeid(condition).name()) << std::endl;

    return 0;
}
$ g++ 02_stored_demangle.cpp -o 02_stored_demangle -std=c++17 -g -O3
Closure example (03_closure.cpp).#
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <iostream>
#include <algorithm>
#include <vector>
#include <functional>

int main(int argc, char ** argv)
{
    std::vector<int> data(63712);
    for (size_t i=0 ; i<data.size(); ++i) { data[i] = i;}

    int divisor = 23;

#if WRONG_CAPTURE
    std::cout
        << "Count (wrong capture): "
        << std::count_if(data.begin(), data.end(),
                         [](int v){ return 0 == v%divisor; })
        << " (divisor: " << divisor << ")"
        << std::endl;
#endif

    std::cout
        << "Count (lambda explicitly capture by value): "
        << std::count_if(data.begin(), data.end(),
                         [divisor](int v){ return 0 == v%divisor; })
        << " (divisor: " << divisor << ")"
        << std::endl;

    std::cout
        << "Count (lambda implicitly capture by value): "
        << std::count_if(data.begin(), data.end(),
                         [=](int v){ return 0 == v%divisor; })
        << " (divisor: " << divisor << ")"
        << std::endl;

    std::cout
        << "Count (lambda explicitly capture by reference): "
        << std::count_if(data.begin(), data.end(),
                         [&divisor](int v){ divisor = 10; return 0 == v%divisor; })
        << " (divisor: " << divisor << ")"
        << std::endl;

    return 0;
}
$ g++ 03_closure.cpp -o 03_closure -std=c++17 -g -O3 -DWRONG_CAPTURE
$ g++ 03_closure.cpp -o 03_closure -std=c++17 -g -O3