<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>C++14 | Ziji's Homepage</title><link>https://zijishi.xyz/tag/c-14/</link><atom:link href="https://zijishi.xyz/tag/c-14/index.xml" rel="self" type="application/rss+xml"/><description>C++14</description><generator>Hugo Blox Builder (https://hugoblox.com)</generator><language>en-us</language><copyright>Ziji Shi © 2025</copyright><lastBuildDate>Mon, 08 Jan 2018 01:03:02 +0800</lastBuildDate><image><url>https://zijishi.xyz/media/icon_hu_926934747de47144.png</url><title>C++14</title><link>https://zijishi.xyz/tag/c-14/</link></image><item><title>Function Pointers, Lambdas, and std::function in Modern C++</title><link>https://zijishi.xyz/post/todo/learn-function-pointer-and-lambda/</link><pubDate>Mon, 08 Jan 2018 01:03:02 +0800</pubDate><guid>https://zijishi.xyz/post/todo/learn-function-pointer-and-lambda/</guid><description>&lt;p&gt;Function pointers and lambdas are two ways to treat a function as a value — something you can store in a variable, pass to another function, or return from a function. In this post I explain both, how they relate to &lt;code&gt;std::function&lt;/code&gt;, and when to reach for each one.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="function-pointers"&gt;Function Pointers&lt;/h2&gt;
&lt;p&gt;A function pointer holds the address of a function. Calling through a function pointer dispatches to whichever function is stored at runtime.&lt;/p&gt;
&lt;h3 id="basic-syntax"&gt;Basic Syntax&lt;/h3&gt;
&lt;p&gt;The declaration syntax is famously confusing. The key rule: the pointer name sits in the middle, wrapped in parentheses, with the return type on the left and parameter types on the right.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-cpp"&gt;int add(int a, int b) { return a + b; }
int mul(int a, int b) { return a * b; }
// Declare a pointer to a function that takes two ints and returns int
int (*op)(int, int);
op = add; // point at add
op(3, 4); // returns 7
op = mul; // point at mul
op(3, 4); // returns 12
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can skip the &lt;code&gt;&amp;amp;&lt;/code&gt; when assigning a function — a function name decays to a pointer to itself, the same way an array name decays to a pointer to its first element.&lt;/p&gt;
&lt;h3 id="cleaning-up-the-syntax-with-using"&gt;Cleaning Up the Syntax with &lt;code&gt;using&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;The raw pointer syntax scales poorly when used as a parameter type. Use a type alias:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-cpp"&gt;using BinaryOp = int (*)(int, int);
int apply(BinaryOp op, int a, int b) {
return op(a, b);
}
apply(add, 3, 4); // 7
apply(mul, 3, 4); // 12
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="passing-functions-as-callbacks"&gt;Passing Functions as Callbacks&lt;/h3&gt;
&lt;p&gt;Function pointers are the classic C-style callback mechanism. The C standard library uses them throughout:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-cpp"&gt;#include &amp;lt;cstdlib&amp;gt;
int cmp(const void* a, const void* b) {
return *(int*)a - *(int*)b;
}
int arr[] = {5, 2, 8, 1};
qsort(arr, 4, sizeof(int), cmp); // sorts in place
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="limitations"&gt;Limitations&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Can only point to &lt;strong&gt;free functions&lt;/strong&gt; (or &lt;code&gt;static&lt;/code&gt; member functions). Cannot point to a non-static member function or capture local state.&lt;/li&gt;
&lt;li&gt;No inline opportunity at a call site unless the compiler can prove at compile time which function is pointed to.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="lambdas-c11"&gt;Lambdas (C++11)&lt;/h2&gt;
&lt;p&gt;A lambda is an anonymous function object defined inline. The compiler synthesizes a unique struct for each lambda, with &lt;code&gt;operator()&lt;/code&gt; containing your lambda body. This means lambdas are zero-cost abstractions when used with templates — the compiler can see the full body and inline it.&lt;/p&gt;
&lt;h3 id="basic-syntax-1"&gt;Basic Syntax&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;[capture](parameters) -&amp;gt; return_type { body }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;-&amp;gt; return_type&lt;/code&gt; is optional; the compiler deduces it from the &lt;code&gt;return&lt;/code&gt; statement.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-cpp"&gt;auto square = [](int x) { return x * x; };
square(5); // 25
// Used inline with std::sort
vector&amp;lt;int&amp;gt; v = {5, 2, 8, 1};
sort(v.begin(), v.end(), [](int a, int b) { return a &amp;lt; b; });
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="capture-clauses"&gt;Capture Clauses&lt;/h3&gt;
&lt;p&gt;The capture clause &lt;code&gt;[...]&lt;/code&gt; controls which local variables from the enclosing scope the lambda can access.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capture&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Capture nothing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[=]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Capture all locals by value (copy)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[&amp;amp;]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Capture all locals by reference&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[x]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Capture &lt;code&gt;x&lt;/code&gt; by value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[&amp;amp;x]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Capture &lt;code&gt;x&lt;/code&gt; by reference&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[=, &amp;amp;x]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Capture all by value except &lt;code&gt;x&lt;/code&gt; by reference&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre&gt;&lt;code class="language-cpp"&gt;int threshold = 10;
// By value: lambda gets its own copy of threshold
auto above_val = [threshold](int x) { return x &amp;gt; threshold; };
// By reference: lambda reads threshold through a reference
// (careful: if threshold goes out of scope, this is UB)
auto above_ref = [&amp;amp;threshold](int x) { return x &amp;gt; threshold; };
threshold = 20;
above_val(15); // still uses 10 → false
above_ref(15); // uses current threshold (20) → false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Prefer explicit captures (&lt;code&gt;[x]&lt;/code&gt;, &lt;code&gt;[&amp;amp;x]&lt;/code&gt;) over blanket &lt;code&gt;[=]&lt;/code&gt; or &lt;code&gt;[&amp;amp;]&lt;/code&gt; — they make it clear exactly what state the lambda depends on.&lt;/p&gt;
&lt;h3 id="mutable-lambdas"&gt;Mutable Lambdas&lt;/h3&gt;
&lt;p&gt;By default, variables captured by value are &lt;code&gt;const&lt;/code&gt; inside the lambda body. Use &lt;code&gt;mutable&lt;/code&gt; to allow modification of the copy:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-cpp"&gt;int count = 0;
auto increment = [count]() mutable { return ++count; };
increment(); // 1 (modifies the lambda's own copy)
increment(); // 2
count; // still 0 — original is untouched
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="generic-lambdas-c14"&gt;Generic Lambdas (C++14)&lt;/h3&gt;
&lt;p&gt;Use &lt;code&gt;auto&lt;/code&gt; parameters to write a lambda that works for multiple types:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-cpp"&gt;auto add = [](auto a, auto b) { return a + b; };
add(1, 2); // int: 3
add(1.5, 2.5); // double: 4.0
add(string(&amp;quot;hello &amp;quot;), string(&amp;quot;world&amp;quot;)); // string concatenation
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The compiler generates a separate template instantiation for each distinct set of argument types — identical to a function template.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="stdfunction"&gt;&lt;code&gt;std::function&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;std::function&amp;lt;R(Args...)&amp;gt;&lt;/code&gt; is a type-erased callable wrapper. It can hold &lt;strong&gt;any callable&lt;/strong&gt; with a compatible signature: a free function, a lambda (with or without captures), a functor, or a bound member function.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-cpp"&gt;#include &amp;lt;functional&amp;gt;
using BinaryOp = function&amp;lt;int(int, int)&amp;gt;;
BinaryOp op;
op = add; // free function
op = [](int a, int b) { return a * b; }; // lambda
op = multiplier_object; // functor with operator()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="when-to-use-stdfunction"&gt;When to Use &lt;code&gt;std::function&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Use it when you need to &lt;strong&gt;store&lt;/strong&gt; a callable in a struct or class member, or return one from a function, and the concrete type varies at runtime:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-cpp"&gt;struct Button {
function&amp;lt;void()&amp;gt; on_click;
};
Button b;
b.on_click = []() { cout &amp;lt;&amp;lt; &amp;quot;clicked!\n&amp;quot;; };
b.on_click(); // dispatches at runtime
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="the-performance-cost"&gt;The Performance Cost&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;std::function&lt;/code&gt; is not free:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Type erasure overhead&lt;/strong&gt;: uses an internal virtual-dispatch-like mechanism to call through to the stored callable.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Possible heap allocation&lt;/strong&gt;: if the stored callable is larger than a small internal buffer (implementation-defined, typically 16–32 bytes), &lt;code&gt;std::function&lt;/code&gt; heap-allocates it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cannot be inlined&lt;/strong&gt;: the compiler cannot see through &lt;code&gt;std::function&lt;/code&gt; to inline the body.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Prefer a template parameter over &lt;code&gt;std::function&lt;/code&gt; in performance-critical code:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-cpp"&gt;// Fast: compiler instantiates a version with the exact lambda type and can inline
template &amp;lt;typename F&amp;gt;
int apply(F op, int a, int b) {
return op(a, b);
}
// Slower: virtual dispatch, possible allocation, no inlining
int apply(function&amp;lt;int(int,int)&amp;gt; op, int a, int b) {
return op(a, b);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id="how-they-relate"&gt;How They Relate&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt; ┌──────────────────────────────┐
│ std::function │ ← holds any callable
│ (type erasure, heap alloc) │
└──────────────┬───────────────┘
│ wraps
┌────────────────────┼────────────────────┐
▼ ▼ ▼
free function lambda [] lambda [x]/[&amp;amp;]
(function ptr) (no capture → can (has state →
convert to fn ptr) cannot convert)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Key rules:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A lambda with an &lt;strong&gt;empty capture&lt;/strong&gt; &lt;code&gt;[]&lt;/code&gt; is implicitly convertible to a function pointer of matching type.&lt;/li&gt;
&lt;li&gt;A lambda with any capture is &lt;strong&gt;not&lt;/strong&gt; convertible to a function pointer — it carries state and has no single address.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;std::function&lt;/code&gt; wraps all of the above at the cost of type erasure.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class="language-cpp"&gt;using Op = int (*)(int, int);
Op p = [](int a, int b) { return a + b; }; // OK: no capture
int x = 5;
Op q = [x](int a, int b) { return a + x; }; // ERROR: has capture
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Function pointer&lt;/th&gt;
&lt;th&gt;Lambda&lt;/th&gt;
&lt;th&gt;&lt;code&gt;std::function&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Holds state?&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (via capture)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inlineable?&lt;/td&gt;
&lt;td&gt;Rarely&lt;/td&gt;
&lt;td&gt;Yes (with templates)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Syntax&lt;/td&gt;
&lt;td&gt;Verbose&lt;/td&gt;
&lt;td&gt;Concise&lt;/td&gt;
&lt;td&gt;Concise&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Overhead&lt;/td&gt;
&lt;td&gt;Indirect call&lt;/td&gt;
&lt;td&gt;Zero (template)&lt;/td&gt;
&lt;td&gt;Type erasure + possible alloc&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use when&lt;/td&gt;
&lt;td&gt;C APIs, callbacks&lt;/td&gt;
&lt;td&gt;Local use, STL algorithms&lt;/td&gt;
&lt;td&gt;Storing mixed callables&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Default advice:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use a &lt;strong&gt;lambda&lt;/strong&gt; for STL algorithms and local callbacks.&lt;/li&gt;
&lt;li&gt;Use a &lt;strong&gt;template parameter&lt;/strong&gt; (&lt;code&gt;template &amp;lt;typename F&amp;gt;&lt;/code&gt;) when writing a higher-order function you want the compiler to optimize.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;std::function&lt;/code&gt; only when you need to store a callable whose concrete type is not known at compile time (e.g., event handlers, plugin systems).&lt;/li&gt;
&lt;li&gt;Use a &lt;strong&gt;function pointer&lt;/strong&gt; when interfacing with C APIs that require one.&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>