aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/development
diff options
context:
space:
mode:
authorJohannes Stoelp <johannes.stoelp@gmail.com>2023-05-29 15:58:48 +0200
committerJohannes Stoelp <johannes.stoelp@gmail.com>2023-05-29 15:58:48 +0200
commit08f554df4ee9a68a0e4694ba9930561ecc196ecd (patch)
tree25223530cdd39131fbcc79cfbb513bffeb28ab07 /src/development
parent80fa1be3da1b5a20042eccd04df57b01a30a7744 (diff)
downloadnotes-08f554df4ee9a68a0e4694ba9930561ecc196ecd.tar.gz
notes-08f554df4ee9a68a0e4694ba9930561ecc196ecd.zip
c++: perfect fwd example
Diffstat (limited to 'src/development')
-rw-r--r--src/development/c++.md5
-rw-r--r--src/development/c++/Makefile2
-rw-r--r--src/development/c++/fwd-perfect.cc82
3 files changed, 88 insertions, 1 deletions
diff --git a/src/development/c++.md b/src/development/c++.md
index 655a603..2fc7a09 100644
--- a/src/development/c++.md
+++ b/src/development/c++.md
@@ -93,6 +93,11 @@ A more detailed description is available in the SO discussion [How does
{{#include c++/tmpl-pair.cc:3:}}
```
+# Example: Perfect forwarding
+```cpp
+{{#include c++/fwd-perfect.cc:3:}}
+```
+
[gist-strict-asliasing]: https://gist.github.com/shafik/848ae25ee209f698763cffee272a58f8
[parameter-pack]: https://en.cppreference.com/w/cpp/language/parameter_pack
[enable-if]: https://en.cppreference.com/w/cpp/types/enable_if
diff --git a/src/development/c++/Makefile b/src/development/c++/Makefile
index 36065e4..622bfd3 100644
--- a/src/development/c++/Makefile
+++ b/src/development/c++/Makefile
@@ -1,4 +1,4 @@
-SRC = concepts-11.cc meta.cc meta2.cc meta4.cc tmpl-pair.cc tmpl-void_t.cc fwd.cc
+SRC = concepts-11.cc meta.cc meta2.cc meta4.cc tmpl-pair.cc tmpl-void_t.cc fwd.cc fwd-perfect.cc
BIN = $(SRC:.cc=)
all: $(BIN)
diff --git a/src/development/c++/fwd-perfect.cc b/src/development/c++/fwd-perfect.cc
new file mode 100644
index 0000000..2fafc3e
--- /dev/null
+++ b/src/development/c++/fwd-perfect.cc
@@ -0,0 +1,82 @@
+// Copyright (C) 2023 johannst
+
+#include <cassert>
+#include <cstdio>
+#include <new>
+#include <type_traits>
+#include <utility>
+
+struct S {};
+
+struct M {
+ M() {
+ std::puts("M()");
+ }
+ M(const M&) {
+ std::puts("M(M&)");
+ }
+ M(M&&) {
+ std::puts("M(M&&)");
+ }
+ M& operator=(const M&) = delete;
+ M& operator=(M&&) = delete;
+
+ M(S&, int) {
+ std::puts("M(S&)");
+ }
+ M(S&&, int) {
+ std::puts("M(S&&)");
+ }
+ ~M() {
+ std::puts("~M()");
+ }
+};
+
+template<typename T>
+struct option {
+ static_assert(!std::is_reference_v<T>);
+
+ constexpr option() = default;
+
+ template<typename... Params>
+ constexpr option(Params&&... params) : m_has_val(true) {
+ // BAD: does not perfectly forward!
+ // eg, if option(S&&) is invoked, this would invoke M(S&).
+ // new (&m_val) T(params...);
+
+ // GOOD: perfectly forwards params to constructor of T.
+ new (m_val) T(std::forward<Params>(params)...);
+ }
+
+ ~option() {
+ reset();
+ }
+
+ constexpr T& value() {
+ assert(m_has_val);
+ return *reinterpret_cast<T*>(m_val);
+ }
+
+ private:
+ constexpr void reset() {
+ if (!m_has_val) {
+ return;
+ }
+ if constexpr (!std::is_trivially_destructible_v<T>) {
+ value().~T();
+ };
+ }
+
+ alignas(T) char m_val[sizeof(T)];
+ bool m_has_val{false};
+};
+
+int main() {
+ std::puts("==> case 1");
+ // invokes M(S&&, int)
+ option<M> opt1(S{}, 123);
+
+ std::puts("==> case 2");
+ // invokes M() + M(M&&)
+ option<M> x /* option(M&&) + M(M&&) */ = M{} /* M() */;
+}