I'd like to have let's say getKeys() function getting not-copiable keys out of a map:
class MyObj {
// ... complex, abstract class...
};
struct Comparator { bool operator()(std::unique_ptr<MyObj> const &a, std::unique_ptr<MyObj> const &b); };
std::vector<std::unique_ptr<MyObj>> getKeys(std::map<std::unique_ptr<MyObj>, int, Comparator> &&map) {
std::vector<std::unique_ptr<MyObj>> res;
for (auto &it : map) {
res.push_back(std::move(it.first));
}
return res;
}
But it is not working because the key in it (.first) is const. Any tips how to solve it? Note: In our environment I'm not allowed to use C++17 function std::map::extract().
Is it somehow ok to use const_cast because map will be destructed anyway?
res.push_back(std::move(const_cast<std::unique_ptr<MyObj> &>(it.first)));
I want to avoid cloning MyObj.
I know why keys of a std::map container cannot be modified but is it still disallowed for a map that is going to be destructed immediately after the key modification?
Note: In our environment I'm not allowed to use C++17 function std::map::extract().
Shame - it was introduced to solve this problem.
Is it somehow ok to use const_cast because map will be destructed anyway?
No.
I want to avoid cloning MyObj.
Sorry; you'll need to clone the keys at least.
I know why keys of a std::map container cannot be modified but is it still disallowed for a map that is going to be destructed immediately after the key modification?
Yes.
The map's internal machinery has no way of knowing that its destiny awaits.
Yes, it's still disallowed. Non-const access to keys is probably safe if you're just going to destroy the map afterwards, but it's not guaranteed to be safe by the standard, and the std::map interface doesn't offer any sort of relaxation of the rules which applies to rvalue references.
What std::map does have since C++17, though, is extract(), which rips a key-value pair out of the map entirely and returns it as a "node handle". This node handle provides non-const access to the key. So if you were to move the pointer out of that node handle, the eventual destruction would happen to an empty pointer.
Example:
#include <utility>
#include <memory>
#include <vector>
#include <map>
template <typename K, typename V>
std::vector<K> extractKeys(std::map<K, V> && map)
{
std::vector<K> res;
while(!map.empty())
{
auto handle = map.extract(map.begin());
res.emplace_back(std::move(handle.key()));
}
return std::move(res);
}
int main()
{
std::map<std::unique_ptr<int>, int> map;
map.emplace(std::make_pair(std::make_unique<int>(3), 4));
auto vec = extractKeys(std::move(map));
return *vec[0];
}
Answers persuaded me that I should avoid const_cast-ing. After some analysis I realized that usage of my map is quite isolated in the code so I could do a small refactor to avoid the const-issue.
Here is the result:
class MyObj {
// ... complex, abstract class...
};
struct Comparator { bool operator()(MyObj const *a, MyObj const *b); };
// key is a pointer only, value holds the key object and the effective "value"
struct KeyAndVal { std::unique_ptr<MyObj> key; int val; };
using MyMap = std::map<MyObj *, KeyAndVal, Comparator>;
// Example how emplace should be done
auto myEmplace(MyMap &map, std::unique_ptr<MyObj> key, int val) {
auto *keyRef = key.get(); // to avoid .get() and move in one expr below
return map.emplace(keyRef, KeyAndVal{ std::move(key), val });
}
std::vector<std::unique_ptr<MyObj>> getKeys(MyMap map) {
std::vector<std::unique_ptr<MyObj>> res;
for (auto &it : map) {
res.push_back(std::move(it.second.key));
}
// here 'map' is destroyed but key references are still valid
// (moved into return value).
return res;
}
Related
I need to maintain a priority queue Q of large objects (of type T). Since these objects are expensive to copy, I would like to be able to retrieve a writable object with auto h = std::move(Q.top()). But I can't do this since std::priority_queue<std::unique_ptr<T>>::top() returns only a const reference. Why? (And is there a simple workaround?)
You can store the large objects as unique_ptr<T> in the priority queue. The thing to note is that queue.top() returns a const unique_ptr<T>&, which means that the T itself isn't const. So you can do this:
T obj(std::move(*queue.top()));
queue.pop();
Edit: Since your T doesn't have a move constructor, I'd just bite the bullet a little bit and use a std::shared_ptr<T>:
std::priority_queue<std::shared_ptr<T>, ...> queue;
// fill queue
// No need for anything special.
std::shared_ptr<T> ptr = queue.top();
queue.pop();
You can wrap your large object to additional structure which will have a field to be used by custom compare function, this member should not be affected by move operations, for example it should by some plain data type like int:
struct BigObject {
std::unique_ptr<int> data;
int forCmp;
};
struct Cmp {
bool operator()(const BigObject& lhs, const BigObject& rhs) {
return lhs.forCmp < rhs.forCmp;
}
};
after move(queue.top()) inner order of queue cannot be broken. Moved instance of BigObject still has a valid value of forCmp used by comparator.
Then inherit from priority_queue, by doing this you will have the access to c underyling container, and add front method:
template<class T, class Cmp>
struct Wrapper : std::priority_queue<T,std::vector<T>,Cmp> {
T& front() {
return this->c.front();
}
};
the use:
Wrapper<BigObject,Cmp> q;
BigObject bo;
bo.forCmp = 12;
q.push(std::move(bo));
BigObject i = std::move(q.front());
Full demo
You can inherit from priority_queue and write T pop() to hide void pop()
template <typename T>
T fixed_priority_queue<T>::pop() {
std::pop_heap(c.begin(), c.end(), comp);
T value = std::move(c.back());
c.pop_back();
return value;
}
Is there a way to declare a std::map whose value type is an iterator to itself?
map<string, map<string, (#)>::iterator> myMap;
The above code snippet wouldn't work because the iterator type needs to know the second template argument, marked as (#). (which would be itself).
The intent is to avoid doing unnecessary find operations to access the element that is pointed to by another element — as opposed to using map<string, string>.
Such definition is not possible, since the value type and the iterator type would be mutually infinitely recursive.
It is possible to work around this using a bit of indirection. It is even possible to avoid the dynamic allocation of std::any, and the fact that std::map<K,V> is undefined unless V is complete.
But the solution is a bit tricky, and relies on some assumptions which are reasonable, but not specified by the standard. See comments in the implementation. The main trick is to defer definition of a member variable type until after definition of the enveloping class. This is achieved by reusing raw storage.
Usage first:
int main()
{
Map map;
auto [it, _] = map.emplace("first", iter_wrap{});
map.emplace("maps to first", conv::wrap(it));
// erase first mapping by only looking
// up the element that maps to it
map.erase(conv::it(map.find("maps to first")));
}
Definition
struct NoInitTag {} noInitTag;
class iter_wrap
{
public:
iter_wrap();
~iter_wrap();
iter_wrap(const iter_wrap&);
iter_wrap(iter_wrap&&);
const iter_wrap& operator=(const iter_wrap&);
const iter_wrap& operator=(iter_wrap&&);
private:
// We rely on assumption that all map iterators have the same size and alignment.
// Compiler should hopefully warn if our allocation is insufficient.
using dummy_it = std::map<int, int>::iterator;
static constexpr auto it_size = sizeof(dummy_it);
static constexpr auto it_align = alignof(dummy_it);
alignas(it_align) std::byte store[it_size];
explicit iter_wrap(NoInitTag){}
friend struct conv;
};
using Map = std::map<std::string, iter_wrap>;
using It = Map::iterator;
struct conv {
static constexpr It&
it(iter_wrap&& wrap) noexcept {
return *std::launder(reinterpret_cast<It*>(wrap.store));
}
static constexpr const It&
it(const iter_wrap& wrap) noexcept {
return *std::launder(reinterpret_cast<const It*>(wrap.store));
}
template<class It>
static const iter_wrap
wrap(It&& it) {
iter_wrap iw(noInitTag);
create(iw, std::forward<It>(it));
return iw;
}
template<class... Args>
static void
create(iter_wrap& wrap, Args&&... args) {
new(wrap.store) It(std::forward<Args>(args)...);
}
static constexpr void
destroy(iter_wrap& wrap) {
it(wrap).~It();
}
};
iter_wrap::iter_wrap() {
conv::create(*this);
}
iter_wrap::iter_wrap(const iter_wrap& other) {
conv::create(*this, conv::it(other));
}
iter_wrap::iter_wrap(iter_wrap&& other) {
conv::create(*this, std::move(conv::it(other)));
}
const iter_wrap& iter_wrap::operator=(const iter_wrap& other) {
conv::destroy(*this);
conv::create(*this, conv::it(other));
return *this;
}
const iter_wrap& iter_wrap::operator=(iter_wrap&& other) {
conv::destroy(*this);
conv::create(*this, std::move(conv::it(other)));
return *this;
}
iter_wrap::~iter_wrap() {
conv::destroy(*this);
}
Old answer; This assumed that it was not an important feature to avoid lookups while traversing stored mappings.
It appears that the data structure that you attempt to represent is a set of keys (strings), where each key maps to another key of the set. Easier way to represent that is to separate those two aspects:
using Set = std::set<std::string>;
using Map = std::map<Set::iterator, Set::iterator>;
Note that these two data structures do not automatically stay in sync. An element added to the set doesn't automatically have a mapping to another, and an element erased from the set leaves dangling iterators to the map. As such, it would be wise to write a custom container class that enforces the necessary invariants.
Only through type erasure. For example, you could use std::any
std::map<std::string, std::any> myMap;
auto inserted = myMap.emplace("foo", std::any());
// how it can be populated:
inserted.first->second = inserted.first;
using it_type = decltype(myMap.begin());
// how values can be extracted:
auto it = std::any_cast<it_type>(myMap["foo"]);
EDIT: The following also seems to work (clang-7.0.0 and gcc-8.2), but it is illegal (basically std::map does not specify that incomplete types are allowed):
struct Iter;
using Map = std::map<std::string, Iter>;
struct Iter {
Map::iterator it;
};
Is there a way to declare a std::map whose value type is an iterator to itself?
map<string, map<string, (#)>::iterator> myMap;
The above code snippet wouldn't work because the iterator type needs to know the second template argument, marked as (#). (which would be itself).
The intent is to avoid doing unnecessary find operations to access the element that is pointed to by another element — as opposed to using map<string, string>.
Such definition is not possible, since the value type and the iterator type would be mutually infinitely recursive.
It is possible to work around this using a bit of indirection. It is even possible to avoid the dynamic allocation of std::any, and the fact that std::map<K,V> is undefined unless V is complete.
But the solution is a bit tricky, and relies on some assumptions which are reasonable, but not specified by the standard. See comments in the implementation. The main trick is to defer definition of a member variable type until after definition of the enveloping class. This is achieved by reusing raw storage.
Usage first:
int main()
{
Map map;
auto [it, _] = map.emplace("first", iter_wrap{});
map.emplace("maps to first", conv::wrap(it));
// erase first mapping by only looking
// up the element that maps to it
map.erase(conv::it(map.find("maps to first")));
}
Definition
struct NoInitTag {} noInitTag;
class iter_wrap
{
public:
iter_wrap();
~iter_wrap();
iter_wrap(const iter_wrap&);
iter_wrap(iter_wrap&&);
const iter_wrap& operator=(const iter_wrap&);
const iter_wrap& operator=(iter_wrap&&);
private:
// We rely on assumption that all map iterators have the same size and alignment.
// Compiler should hopefully warn if our allocation is insufficient.
using dummy_it = std::map<int, int>::iterator;
static constexpr auto it_size = sizeof(dummy_it);
static constexpr auto it_align = alignof(dummy_it);
alignas(it_align) std::byte store[it_size];
explicit iter_wrap(NoInitTag){}
friend struct conv;
};
using Map = std::map<std::string, iter_wrap>;
using It = Map::iterator;
struct conv {
static constexpr It&
it(iter_wrap&& wrap) noexcept {
return *std::launder(reinterpret_cast<It*>(wrap.store));
}
static constexpr const It&
it(const iter_wrap& wrap) noexcept {
return *std::launder(reinterpret_cast<const It*>(wrap.store));
}
template<class It>
static const iter_wrap
wrap(It&& it) {
iter_wrap iw(noInitTag);
create(iw, std::forward<It>(it));
return iw;
}
template<class... Args>
static void
create(iter_wrap& wrap, Args&&... args) {
new(wrap.store) It(std::forward<Args>(args)...);
}
static constexpr void
destroy(iter_wrap& wrap) {
it(wrap).~It();
}
};
iter_wrap::iter_wrap() {
conv::create(*this);
}
iter_wrap::iter_wrap(const iter_wrap& other) {
conv::create(*this, conv::it(other));
}
iter_wrap::iter_wrap(iter_wrap&& other) {
conv::create(*this, std::move(conv::it(other)));
}
const iter_wrap& iter_wrap::operator=(const iter_wrap& other) {
conv::destroy(*this);
conv::create(*this, conv::it(other));
return *this;
}
const iter_wrap& iter_wrap::operator=(iter_wrap&& other) {
conv::destroy(*this);
conv::create(*this, std::move(conv::it(other)));
return *this;
}
iter_wrap::~iter_wrap() {
conv::destroy(*this);
}
Old answer; This assumed that it was not an important feature to avoid lookups while traversing stored mappings.
It appears that the data structure that you attempt to represent is a set of keys (strings), where each key maps to another key of the set. Easier way to represent that is to separate those two aspects:
using Set = std::set<std::string>;
using Map = std::map<Set::iterator, Set::iterator>;
Note that these two data structures do not automatically stay in sync. An element added to the set doesn't automatically have a mapping to another, and an element erased from the set leaves dangling iterators to the map. As such, it would be wise to write a custom container class that enforces the necessary invariants.
Only through type erasure. For example, you could use std::any
std::map<std::string, std::any> myMap;
auto inserted = myMap.emplace("foo", std::any());
// how it can be populated:
inserted.first->second = inserted.first;
using it_type = decltype(myMap.begin());
// how values can be extracted:
auto it = std::any_cast<it_type>(myMap["foo"]);
EDIT: The following also seems to work (clang-7.0.0 and gcc-8.2), but it is illegal (basically std::map does not specify that incomplete types are allowed):
struct Iter;
using Map = std::map<std::string, Iter>;
struct Iter {
Map::iterator it;
};
I am writing some C++ code which wraps the std::unordered_map type, where I want to hide the underlying type and present it as another type. More specifically, I want to wrap the std::pair from the std::unordered_map with another type. For the sake of argument, lets suppose the wrapper looks like this...
template <typename ActualT >
class wrapper final
{
private:
ActualT actual_;
public:
//Some constructors...
typename ActualT::first_type & get_first()
{
return actual_.first;
}
typename ActualT::second_type & get_second()
{
return actual_.second;
}
};
My reasoning is that since the wrapper class only has a member which is the exact type which it is wrapping, converting a reference from the original type to the wrapper type should be fine, but the type compatibility for structs states that the members should have the same type and name for the types to be compatible. Would using type-punning in this fashion potentially cause undefined behaviour or alignment issues?
using my_map = std::unordered_map < int, int >;
my_map m;
//Do some inserts...
reinterpret_cast<wrapper<typename my_map::value_type>&>(*m.find(10)).get_second() = 1.0;
I want client code to be allowed to access the entries of a map without knowing about the pair which is returned by the map. I also want to write a custom forward iterator, hence I need to return a reference to the entry. Would converting the reference to the pair to a reference to a class which act as a wrapper be considered dangerous?
Is there perhaps a better approach to accomplishing this?
This absolutely is undefined behaviour.
Seriously rethink your priorities.
Some free functions of the form
const my_map::key_type & MeaningfulNameHere(my_map::reference)
will go a long way to giving you meaningful names.
If you must wrap the standard library with different names, just use a non-explicit constructor, and store references.
template <typename Map>
class entry final
{
private:
typename Map::reference ref;
public:
entry(Map::reference ref) : ref(ref) {}
const typename Map::key_type & key()
{
return ref.first;
}
typename Map::mapped_type & value()
{
return ref.second;
}
};
If you really need the iterator to dereference to entry you can. But you can just implicitly instantiate entrys from the Map::references returned by Map::iterator::operator*, you don't need a custom iterator.
template <typename Map>
class entry_iterator
{
private:
typename Map::iterator it;
entry<Map> entry;
public:
entry<Map>& operator*() { return entry; }
entry_iterator operator++() { ++it; entry = *it; return *this; }
// etc
}
So you could clean this up, but I wouldn't suggest it:
#include <unordered_map>
#include <iostream>
using namespace std;
template <class Key, class Value>
class wrapper
{
public:
explicit wrapper(std::pair<const Key, Value>& kvp)
: _key{kvp.first}
, _value{kvp.second}
{}
const Key& key() const { return _key; }
Value& value() { return _value; }
private:
const Key& _key;
Value& _value;
};
int main()
{
unordered_map<int,int> m;
m[1] = 1;
m[3] = 3;
auto it = m.find(1);
wrapper w{*it};
w.value() = 30;
std::cout << w.key() << " -> " << w.value() << '\n';
}
The above effectively hides the pair from users of your class. It doesn't deal with exceptions (find() returning end() for example), and makes no guarantees about lifetimes. It's marginally better than what you have because it doesn't require a reinterpret_cast to an unrelated type.
However, map, unordered_map, set, etc. storing returning iterators as pairs is just part of library -- it's the canonical form and I don't see the benefit of shielding people from it.
Use case: a buffer of records. Here is the basic idea.
Making it work requires that the constructor of the record struct knows the key, which is used as recordnumber, when an element is added to the map.
Of course, this can be done with more code, but this looks most elegant to me.
Minimally coded:
#include <whatever>
struct record
{
string foo;
record(unsigned irec) { foo=readrecord(irec); }
};
map<unsigned,record>recbuf;
int main()
{
// if the element is not yet in the map, it should be read.
string foo_ten=recbuf[10].foo;
// do something with the result
printf("foo_ten: %s\n",foo_ten.c_str());
return 0;
}
Edit1: code above will not work.
Any ideas how to get this to work?
Edit2:
I derived a mapplus class adding another map::operator[]:
template<class _Kty, class _Ty, class _Pr = less<_Kty>, class _Alloc = allocator<pair<const _Kty, _Ty> > >class mapplus :public map<_Kty, _Ty, _Pr, _Alloc>
{
public:
mapped_type& operator[](const _Kty &_Keyval)
{ // find element matching _Keyval or insert with default mapped
iterator _Where = _Mybase::lower_bound(_Keyval);
if (_Where == _Mybase::end()
|| _Mybase::_Getcomp()(_Keyval, _Mybase::_Key(_Where._Mynode())))
_Where = _Mybase::emplace_hint(_Where,
_Keyval,
_Ty(_Keyval));
return (_Where->second);
}
};
This does work. I am still interested in comments pointing out to me that I did this in a needlessly complex etc. way. Did I? Can it be done with less ado?
So, you want record objects to be constructed using your record(unsigned) constructor, rather than the default constructor.
Unfortunately, there's no way to do this with operator[] (reference):
If k matches the key of an element in the container, the function
returns a reference to its mapped value.
If k does not match the key of any element in the container, the
function inserts a new element with that key and returns a reference
to its mapped value. Notice that this always increases the container
size by one, even if no mapped value is assigned to the element (the
element is constructed using its default constructor).
I would not recommend overloading operator[] for std::map, for me it seems like a bad design.
However, you can do it with other methods, like insert or emplace (C++11).
See, for example, this answer: Using std::map<K,V> where V has no usable default constructor
Tested example:
#include <map>
#include <sstream>
#include <iostream>
std::string readRecord(int id)
{
std::stringstream stream;
stream << id;
return stream.str();
}
struct Record
{
Record(int id)
: foo(readRecord(id))
{
}
std::string foo;
};
int main()
{
std::map<int, Record> m;
for (int i = 0; i < 10; ++i)
m.insert(std::make_pair(i, Record(i)));
std::cout << m.at(5).foo << std::endl;
// std::cout << m[7].foo << std::endl; // error 'Record::Record': no appropriate default constructor available
return 0;
}