Here is what I am trying to do and what I have tried:
#include <memory>
#include <map>
#include <string>
using namespace std;
struct MyStruct {
const unique_ptr<int> a;
const unique_ptr<int> b;
};
int main() {
auto a = make_unique<int>(7);
auto b = make_unique<int>(5);
map<string, MyStruct> myMap;
myMap["ab"] = { move(a), move(b) }; // nope
myMap.insert("ab", { move(a), move(b) }); // nope
myMap.emplace("ab", { move(a), move(b) }); // nope
myMap.try_emplace("ab", { move(a), move(b) }); // nope
// EDIT 1:
myMap.emplace(piecewise_construct,
forward_as_tuple("ab"),
forward_as_tuple(move(a), move(b))
); // nope
// EDIT 2:
// works in C 20 as there is now a standard ctor for MyStruct:
myMap.try_emplace("ab", move(a), move(b));
return 0;
}
I understand why none of this works but is there any way to insert an element into myMap without modifying MyStruct?
CodePudding user response:
Both
myMap.emplace(piecewise_construct,
forward_as_tuple("ab"),
forward_as_tuple(move(a), move(b))
);
and
myMap.try_emplace("ab", move(a), move(b));
should work in C 20. You need to construct the pair in-place, because it will be non-copyable and non-movable due to MyStruct
. This leaves only the emplace
family of member functions. Using a braced initializer list in emplace
can never work because emplace
only forwards arguments, but wouldn't be able to deduce types for arguments.
Before C 20, these member functions also won't work, because they internally use direct-initialization with parentheses. MyStruct
however has no constructor matching the two arguments. In C 20 parenthesized initializers can also do aggregate initialization and that will work, since MyStruct
is an aggregate.
I don't think there is any possibility to add an element to the map before C 20 without change of MyStruct
or adding an implicit conversion for it, because it can only be aggregate-initialized, but must be emplace
constructed which doesn't support aggregate-initialization.
The only the exception to that should be default initialization. For example
myMap.emplace(piecewise_construct,
forward_as_tuple("ab"),
forward_as_tuple()
);
should work also before C 20, because MyStruct
is default-constructible.
An implicit conversion could be added to do aggregate initialization, for example:
struct A {
std::unique_ptr<int> a;
std::unique_ptr<int> b;
operator MyStruct() && {
return {std::move(a), std::move(b)};
}
};
//...
myMap.emplace("ab", A{ std::move(a), std::move(b) });
This will probably work in C 17 mode on compilers, although it is technically not correct as mandatory copy elision doesn't apply to initialization by conversion operator. See CWG 2327.
As for changes of MyStruct
: In particular the two examples quoted above should also work pre-C 20 if you add a matching constructor:
struct MyStruct {
MyStruct(std::unique_ptr<int> a_, std::unique_ptr<int> b_) : a(std::move(a_)), b(std::move(b_)) { }
const std::unique_ptr<int> a;
const std::unique_ptr<int> b;
};
CodePudding user response:
Move semantics dont work with constant data.
And you need overload ur struct operators. https://en.cppreference.com/w/cpp/language/rule_of_three