emplace_by_calling.h 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #ifndef CARBON_COMMON_EMPLACE_BY_CALLING_H_
  5. #define CARBON_COMMON_EMPLACE_BY_CALLING_H_
  6. #include <type_traits>
  7. #include <utility>
  8. namespace Carbon {
  9. // A utility to use when calling an `emplace` function to emplace the result of
  10. // a function call. Expected usage is:
  11. //
  12. // my_widget_vec.emplace_back(EmplaceByCalling([&] {
  13. // return ConstructAWidget(...);
  14. // }));
  15. //
  16. // In this example, the result of `ConstructAWidget` will be constructed
  17. // directly into the new element of `my_widget_vec`, without performing a copy
  18. // or move.
  19. //
  20. // Note that the type of the argument to `emplace_back` is an `EmplaceByCalling`
  21. // instance, not the type `DestT` stored in the container. When the `DestT`
  22. // instance is eventually initialized directly from the `EmplaceByCalling`, a
  23. // conversion function on `EmplaceByCalling` is used that converts to the type
  24. // `DestT` being emplaced. This `DestT` initialization does not call an
  25. // additional `DestT` copy or move constructor to initialize the result, and
  26. // instead initializes it in-place in the container's storage, per the C++17
  27. // guaranteed copy elision rules. Similarly, within the conversion function, the
  28. // result is initialized directly by calling `make_fn`, again relying on
  29. // guaranteed copy elision.
  30. //
  31. // Because the make function is called from the conversion function,
  32. // `EmplaceByCalling` should only be used in contexts where it will be used to
  33. // initialize a `DestT` object exactly once. This is generally true of `emplace`
  34. // functions. Also, because the `make_fn` callback will be called after the
  35. // container has made space for the new element, it should not inspect or modify
  36. // the container that is being emplaced into.
  37. template <typename MakeFnT>
  38. class EmplaceByCalling {
  39. public:
  40. explicit(false) EmplaceByCalling(MakeFnT make_fn)
  41. : make_fn_(std::move(make_fn)) {}
  42. // Convert to the exact return type of the make function, by calling the make
  43. // function to construct the result. No implicit conversions are permitted
  44. // here, as that would mean we are not constructing the result in place.
  45. template <typename DestT>
  46. requires std::same_as<DestT, std::invoke_result_t<MakeFnT&&>>
  47. // NOLINTNEXTLINE(google-explicit-constructor)
  48. explicit(false) operator DestT() && {
  49. return std::move(make_fn_)();
  50. }
  51. private:
  52. MakeFnT make_fn_;
  53. };
  54. template <typename MakeFnT>
  55. EmplaceByCalling(MakeFnT) -> EmplaceByCalling<MakeFnT>;
  56. } // namespace Carbon
  57. #endif // CARBON_COMMON_EMPLACE_BY_CALLING_H_