C++:将元素插入 std::map<MyStruct>,其中 MyStruct 只能进行聚合初始化并包含常量唯一指针
C++: insert element into std::map<MyStruct> where MyStruct can only be aggregate initialized and contains const unique pointers
这是我正在尝试做的以及我已经尝试过的事情:
#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;
}
我明白为什么 none 这行得通,但是有什么方法可以在不修改 MyStruct 的情况下将元素插入到 myMap 中吗?
移动语义不适用于常量数据。
并且您需要重载您的结构运算符。
https://en.cppreference.com/w/cpp/language/rule_of_three
两者都
myMap.emplace(piecewise_construct,
forward_as_tuple("ab"),
forward_as_tuple(move(a), move(b))
);
和
myMap.try_emplace("ab", move(a), move(b));
应该在 C++20 中工作。您需要构建 in-place 对,因为 non-copyable 和 non-movable 由于 MyStruct
。这只剩下 emplace
族成员函数。在 emplace
中使用花括号初始化器列表永远行不通,因为 emplace
只能转发参数,但无法推断参数的类型。
在C++20之前,这些成员函数也不起作用,因为它们在内部使用带括号的direct-initialization。 MyStruct
但是没有匹配这两个参数的构造函数。在 C++20 中,带括号的初始化器也可以进行聚合初始化,这会起作用,因为 MyStruct
是一个聚合。
我认为在不改变 MyStruct
或为其添加隐式转换的情况下,不可能在 C++20 之前向映射添加元素,因为它只能是 aggregate-initialized, 但必须 emplace
构造不支持 aggregate-initialization.
唯一的例外应该是默认初始化。例如
myMap.emplace(piecewise_construct,
forward_as_tuple("ab"),
forward_as_tuple()
);
也应该在 C++20 之前工作,因为 MyStruct
是 default-constructible.
可以添加隐式转换来进行聚合初始化,例如:
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) });
这可能会在编译器的 C++17 模式下工作,尽管它在技术上是不正确的,因为强制复制省略不适用于转换运算符的初始化。参见 CWG 2327。
至于 MyStruct
的变化:特别是上面引用的两个例子也应该工作 pre-C++20 如果你添加一个匹配的构造函数:
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;
};
这是我正在尝试做的以及我已经尝试过的事情:
#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;
}
我明白为什么 none 这行得通,但是有什么方法可以在不修改 MyStruct 的情况下将元素插入到 myMap 中吗?
移动语义不适用于常量数据。
并且您需要重载您的结构运算符。 https://en.cppreference.com/w/cpp/language/rule_of_three
两者都
myMap.emplace(piecewise_construct,
forward_as_tuple("ab"),
forward_as_tuple(move(a), move(b))
);
和
myMap.try_emplace("ab", move(a), move(b));
应该在 C++20 中工作。您需要构建 in-place 对,因为 non-copyable 和 non-movable 由于 MyStruct
。这只剩下 emplace
族成员函数。在 emplace
中使用花括号初始化器列表永远行不通,因为 emplace
只能转发参数,但无法推断参数的类型。
在C++20之前,这些成员函数也不起作用,因为它们在内部使用带括号的direct-initialization。 MyStruct
但是没有匹配这两个参数的构造函数。在 C++20 中,带括号的初始化器也可以进行聚合初始化,这会起作用,因为 MyStruct
是一个聚合。
我认为在不改变 MyStruct
或为其添加隐式转换的情况下,不可能在 C++20 之前向映射添加元素,因为它只能是 aggregate-initialized, 但必须 emplace
构造不支持 aggregate-initialization.
唯一的例外应该是默认初始化。例如
myMap.emplace(piecewise_construct,
forward_as_tuple("ab"),
forward_as_tuple()
);
也应该在 C++20 之前工作,因为 MyStruct
是 default-constructible.
可以添加隐式转换来进行聚合初始化,例如:
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) });
这可能会在编译器的 C++17 模式下工作,尽管它在技术上是不正确的,因为强制复制省略不适用于转换运算符的初始化。参见 CWG 2327。
至于 MyStruct
的变化:特别是上面引用的两个例子也应该工作 pre-C++20 如果你添加一个匹配的构造函数:
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;
};