To demonstrate my problem, consider this simple program that does not compile:
#include <boost/noncopyable.hpp>
#include <unordered_map>
class foo : boost::noncopyable { };
int main()
{
std::unordered_map<int, foo> m;
auto & element = m[0];
return 0;
}
Using the current version of boost (1.52), Visual Studio 2012 returns the error:
cannot access private member declared in class 'boost::noncopyable_::noncopyable
.
The operator [] for std::unordered_map returns a reference to the element at the provided key, which at first glance seems like it should work -- I've asked for a reference to the element, not a copy of it.
My understanding of the problem is this (which might be wrong, as I haven't used C++ in a while). If the key is not found, unordered_map creates a new element and returns a reference to the new element. boost::noncopyable defines a (private) copy constructor but not a move constructor, and so a move constructor is not generated by the compiler. In its operator[], std::unordered_map makes use of std::move, but since boost::noncopyable doesn't define a move constructor, it falls back to the copy constructor. Since the copy constructor is private, the compilation fails.
What prompted this post is that I'm trying to create an unordered_map of boost::signal2::signal, which inherits from boost::noncopyable. Short of hacking the boost library, is there a simple workaround I can do? Wrapping the signal in a unique_ptr is an option, but it seems to me I might be doing something wrong here.
I may have posted too soon! It appears impossible to add a subclass of boost::noncopyable to unordered_map. Insert, operator[], and emplace all use either a copy constructor (which is private), or a move operation (which doesn't exist for boost::noncopyable). To me this seems a major limitation. Is it even possible to create an unordered_map that contains boost::noncopyable objects? I'm explicitly not trying to copy them -- I want them to spend their entire lifespan inside the unordered_map.
It's not impossible to use a subclass of boost::noncopyable
in an unordered_map
, you simply have to define a move constructor for you type. C++ does not create a default move constructor if you've made your own copy construct (which is what boost::noncopyable
does). Also, if it did define default move constructor, it would try to call the parent's copy constructor which is private. So you must define a move constructor that doesn't try to call boost::noncopyable
's copy constructor. For example this works fine:
#include <boost/noncopyable.hpp>
#include <unordered_map>
struct foo : public boost::noncopyable
{
foo() = default;
foo(foo&&) {}
};
int main()
{
std::unordered_map<int, foo> m;
auto & element = m[0];
return 0;
}
This likely isn't exactly what you're looking for, but I figured I'd toss it out there. The one thing to note is the second
value of the returned pair from emplace()
, which indicates the second call does not introduce a new member, nor copy over the exiting member.
Again, I don't know if this is closer to what you want, but worth a shot. I likely did something wrong, as I'm not overtly familiar with the C++11 standard library as others. Sorry about that if so.
Finally, please note this is not attempting to address the OP's request of using operator []()
for insert+access. Rather, it attempts to simply get a boost::noncopyable
derivation constructed into an unordered_map<>
. To access you would likely need a combination of the below as well as an initial find()
to determine if the tag exists initially.
Anyway...
#include <boost/noncopyable.hpp>
#include <iostream>
#include <unordered_map>
class Foo : public boost::noncopyable
{
public:
Foo(int value) : value(value) {};
void setValue(int value) { this->value = value; }
int getValue() const { return value; }
private:
int value;
};
int main(int argc, char *argv[])
{
typedef std::unordered_map<std::string, Foo> MyMap;
MyMap mymap;
// throw ("test".1) into the map
auto p = mymap.emplace("test", 1);
auto q = mymap.emplace("test", 2); // should not overwrite the first.
// dump content
cout << p.first->second.getValue() << '(' << p.second << ')' << ' '
<< q.first->second.getValue() << '(' << q.second << ')' << endl;
// modify through the second returned iterator/bool pair.
q.first->second.setValue(3);
// dump again, see if p was also updated.
cout << p.first->second.getValue() << '(' << p.second << ')' << ' '
<< q.first->second.getValue() << '(' << q.second << ')' << endl;
return 0;
}
Output
1(1) 1(0)
3(1) 3(0)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With