c++language-lawyerc++17stdmapaggregate-initialization

Is it possible to do a no-copy emplace into map while using aggregate initialization?


See this answer for how to insert into without making copies of the map value.

std::map emplace without copying value

Continueing from that answer - suppose my Foo type looks something like this:

struct Foo {
  const int& intref_; 
  std::mutex mutex_;
}

Then initialized using aggregate-initialization like this

Foo{7}

or

Foo{7, std::mutex()}

Would it be somehow possible to be emplaced into the map with type ?:

std::map<size_t, Foo> mymap;

I know I could just write a constructor for Foo - but can it be done with aggregate initialization instead ?

Link to compiler explorer:

https://godbolt.org/z/_Fm4k1

Relevant c++ references:

https://en.cppreference.com/w/cpp/container/map/try_emplace

https://en.cppreference.com/w/cpp/language/aggregate_initialization


Solution

  • You may exploit casts to indirect your construction

    template<typename T>
    struct tag { using type = T; };
    
    template<typename F>
    struct initializer
    {
        F f;
        template<typename T>
        operator T() &&
        {
            return std::forward<F>(f)(tag<T>{});
        }
    };
    
    template<typename F>
    initializer(F&&) -> initializer<F>;
    
    template<typename... Args>
    auto initpack(Args&&... args)
    {
        return initializer{[&](auto t) {
            using Ret = typename decltype(t)::type;
            return Ret{std::forward<Args>(args)...};
        }};
    }
    

    And use it as

    struct Foo
    {
      const int& intref_; 
      std::mutex mutex_;
    };
    
    void foo()
    {
        int i = 42;
        std::map<int, Foo> m;
        m.emplace(std::piecewise_construct,
                  std::forward_as_tuple(0),
                  std::forward_as_tuple(initpack(i)));
    }
    

    Note you can't prolong a temporary's lifetime by binding it to a non-stack reference.