diff options
author | Johannes Stoelp <johannes.stoelp@gmail.com> | 2023-05-29 15:58:48 +0200 |
---|---|---|
committer | Johannes Stoelp <johannes.stoelp@gmail.com> | 2023-05-29 15:58:48 +0200 |
commit | 08f554df4ee9a68a0e4694ba9930561ecc196ecd (patch) | |
tree | 25223530cdd39131fbcc79cfbb513bffeb28ab07 /src/development | |
parent | 80fa1be3da1b5a20042eccd04df57b01a30a7744 (diff) | |
download | notes-08f554df4ee9a68a0e4694ba9930561ecc196ecd.tar.gz notes-08f554df4ee9a68a0e4694ba9930561ecc196ecd.zip |
c++: perfect fwd example
Diffstat (limited to 'src/development')
-rw-r--r-- | src/development/c++.md | 5 | ||||
-rw-r--r-- | src/development/c++/Makefile | 2 | ||||
-rw-r--r-- | src/development/c++/fwd-perfect.cc | 82 |
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() */; +} |