当您具有值类型的继承结构时,try_emplace 不起作用(按需要)?
try_emplace doesn't work (as desired) when you have an inheritance structure for the value type?
// A couple of simple structs that inherit AND have different ctor arguments
struct A
{
A(int) {}
};
// Note that "B" is a subclass of "A", which is important below
struct B : A
{
B(int, char) : A(0) // The 0 doesn't matter
{}
// Note that B has no data members that "will be chopped"
};
// A simple map... Note the value type is "A"
// There should be no problem putting B's in here though because they are a subclass of A
// And as noted above, B has no additional members that will be chopped
// (
std::unordered_map<int, A> map;
// This works as expected, because we're trying to emplace an object of type "A"
auto [_, success] = map.try_emplace(
5, // the key
6 // the single argument to A's ctor
);
assert(success);
// This compiles BUT:
// 1. It's attempting to overwrite the key used above (5), and so
// the emplace correctly fails
// 2. Uh oh though -- Obviously a "B" is constructed and destructed though
// the emplace fails
auto [_, success] = map.try_emplace(
5,
B{5, 6} // The map knows the value type as "A", which means I only
// have the option of passing A's ctor args, not B's. This doesn't
// do what I want when I'm emplacing a "B"... In fact, this creates
// an rvalue of type "B" (duh) and then destructs it when the emplace fails.
//
// So my question is: How can I pass the arguments for B's ctor
// to a map that expects an "A" (keep in mind B is a subclass of A)
);
assert(!success);
回复几位发贴者:
我知道如果 B 在地图中存储为 A,它会被切片。这就是为什么我特别提到B中没有额外的数据成员。
是的,我绝对会尝试确保在插入失败时不会构建 B。我让它编译的唯一方法是构造一个 B 然后将它推到地图中。所以这不是地图的错:)
所以 B 和 A 之间唯一真正的区别是它们的 ctor。
您不能将 B 放入 A。
可以将B切片成A,切片是指将B的A部分复制到A对象中,
在 C++ 中,变量是它们的类型。它们不是不同的类型,即使其他类型适合存储。
指针和引用可以引用 class 的基础 class 组件。但价值观始终如一。
无法在该映射中存储 B。
现在,如果您可以切片,我们可以推迟 B 的构造,直到 emplace 为它找到一个位置。然后将构建的 B 切片,将 A 部分移入存储,然后销毁。
template<class F>
struct ctor{
F f;
template<class T>
operator T()&&{
return std::move(f)();
}
};
template<class F>
ctor(F)->ctor<F>;
现在只是:
auto [_, success] = map.try_emplace(
5,
ctor{[&]{return B{5, 6};}}
};
So my question is: How can I pass the arguments for B's ctor to a map that expects an "A" (keep in mind B is a subclass of A)
你不能。作为一种解决方法,您可以先检查节点将被插入的位置,然后如果不存在,则将其用作插入元素的提示:
const auto it = map.lower_bound(5);
if (it == map.end() || it->first != 5) {
map.insert(it, std::make_pair(5, B{5, 6}));
}
使用插入提示可以避免再次沿着树走下去。
让我们稍微简化一下您的示例。
void foo(bool b, int* a) {
if (b) return;
for (int i = 0; i < 1000; ++i)
a[i] = i + 42;
}
foo (rand() == 42, std::array<int, 1000>().data());
如果 foo
无法使用它,您认为编译器会忽略创建 std::array<int, 1000>
吗?
如果您希望仅当 foo
超过第一个 return
语句时才创建大数组,那么只有当 foo
超过时才创建大数组由您决定通过第一个 return
语句。有很多方法可以做到这一点,但希望编译器会神奇地为您完成它不是其中之一。您需要更改 foo
以便它在需要时创建一个大数组。
回到您的示例,您已通过编写 B{5, 6}
请求创建类型为 B
的临时对象,并且您已获得类型为 B
的临时对象,无论 emplace
是否成功。对于您传递给 emplace
.
的任何其他对象都是如此
map.try_emplace(5, 5); // 5 is always constructed
map.try_emplace(5, A{5}); // A{5} is always constructed
map.try_emplace(5, B{5, 6}); // B{5, 6} is always constructed
如果你想只在emplace
成功时构造一个B
对象,你需要将B
的创建移动到emplace
时执行的一段代码] 成功。 A
的构造函数是一个很好的地方。
切换到诸如 Haskell 之类的惰性求值语言是该问题的另一种(激进的)解决方案。
// A couple of simple structs that inherit AND have different ctor arguments
struct A
{
A(int) {}
};
// Note that "B" is a subclass of "A", which is important below
struct B : A
{
B(int, char) : A(0) // The 0 doesn't matter
{}
// Note that B has no data members that "will be chopped"
};
// A simple map... Note the value type is "A"
// There should be no problem putting B's in here though because they are a subclass of A
// And as noted above, B has no additional members that will be chopped
// (
std::unordered_map<int, A> map;
// This works as expected, because we're trying to emplace an object of type "A"
auto [_, success] = map.try_emplace(
5, // the key
6 // the single argument to A's ctor
);
assert(success);
// This compiles BUT:
// 1. It's attempting to overwrite the key used above (5), and so
// the emplace correctly fails
// 2. Uh oh though -- Obviously a "B" is constructed and destructed though
// the emplace fails
auto [_, success] = map.try_emplace(
5,
B{5, 6} // The map knows the value type as "A", which means I only
// have the option of passing A's ctor args, not B's. This doesn't
// do what I want when I'm emplacing a "B"... In fact, this creates
// an rvalue of type "B" (duh) and then destructs it when the emplace fails.
//
// So my question is: How can I pass the arguments for B's ctor
// to a map that expects an "A" (keep in mind B is a subclass of A)
);
assert(!success);
回复几位发贴者:
我知道如果 B 在地图中存储为 A,它会被切片。这就是为什么我特别提到B中没有额外的数据成员。
是的,我绝对会尝试确保在插入失败时不会构建 B。我让它编译的唯一方法是构造一个 B 然后将它推到地图中。所以这不是地图的错:)
所以 B 和 A 之间唯一真正的区别是它们的 ctor。
您不能将 B 放入 A。
可以将B切片成A,切片是指将B的A部分复制到A对象中,
在 C++ 中,变量是它们的类型。它们不是不同的类型,即使其他类型适合存储。
指针和引用可以引用 class 的基础 class 组件。但价值观始终如一。
无法在该映射中存储 B。
现在,如果您可以切片,我们可以推迟 B 的构造,直到 emplace 为它找到一个位置。然后将构建的 B 切片,将 A 部分移入存储,然后销毁。
template<class F>
struct ctor{
F f;
template<class T>
operator T()&&{
return std::move(f)();
}
};
template<class F>
ctor(F)->ctor<F>;
现在只是:
auto [_, success] = map.try_emplace(
5,
ctor{[&]{return B{5, 6};}}
};
So my question is: How can I pass the arguments for B's ctor to a map that expects an "A" (keep in mind B is a subclass of A)
你不能。作为一种解决方法,您可以先检查节点将被插入的位置,然后如果不存在,则将其用作插入元素的提示:
const auto it = map.lower_bound(5);
if (it == map.end() || it->first != 5) {
map.insert(it, std::make_pair(5, B{5, 6}));
}
使用插入提示可以避免再次沿着树走下去。
让我们稍微简化一下您的示例。
void foo(bool b, int* a) {
if (b) return;
for (int i = 0; i < 1000; ++i)
a[i] = i + 42;
}
foo (rand() == 42, std::array<int, 1000>().data());
如果 foo
无法使用它,您认为编译器会忽略创建 std::array<int, 1000>
吗?
如果您希望仅当 foo
超过第一个 return
语句时才创建大数组,那么只有当 foo
超过时才创建大数组由您决定通过第一个 return
语句。有很多方法可以做到这一点,但希望编译器会神奇地为您完成它不是其中之一。您需要更改 foo
以便它在需要时创建一个大数组。
回到您的示例,您已通过编写 B{5, 6}
请求创建类型为 B
的临时对象,并且您已获得类型为 B
的临时对象,无论 emplace
是否成功。对于您传递给 emplace
.
map.try_emplace(5, 5); // 5 is always constructed
map.try_emplace(5, A{5}); // A{5} is always constructed
map.try_emplace(5, B{5, 6}); // B{5, 6} is always constructed
如果你想只在emplace
成功时构造一个B
对象,你需要将B
的创建移动到emplace
时执行的一段代码] 成功。 A
的构造函数是一个很好的地方。
切换到诸如 Haskell 之类的惰性求值语言是该问题的另一种(激进的)解决方案。