将映射迭代器覆盖为 return dynamic_casted 值
Overriding map iterator to return dynamic_casted value
编辑:我现在有一个可行的解决方案,https://wandbox.org/permlink/joS14MzfmmayNRd3,只是想知道它是否可以改进。
我有一个映射 map<int, unique_ptr<Base>>
,它的值也可以是 unique_ptr<Derived>
类型。我有包装器 类 A 和 B,它们分别在映射中存储 Base 和 Derived(所有 Base 或所有 Derived 实例)。
我想介绍 A 和 B 的映射语义(引用绑定),其工作方式如下:
// a is of type A, all values in map are of type Base
for (auto& [k, v] : a) {
// v should be of type Base&
}
// b is of type B, all values in map are type Derived
for (auto& [k, v] : b) {
// v should be of type Derived&
// can call functions that are in Derived but not in Base
}
我发现 boost 的 transform_iterator 和 iterator_adaptor 可能会有用(如果有办法不用 boost 就更好),但我似乎没有正确使用它们.正确的做法是什么?
#include <memory>
#include <boost/iterator/transform_iterator.hpp>
#include <vector>
#include <unordered_map>
#include <iostream>
using namespace std;
class Base {
public:
Base(int k) : i(k) {}
virtual ~Base() {}
virtual int getV() {
return i;
}
int i = 0;
};
class Derived : public Base {
public:
Derived(int k) : Base(k) {}
int getV() override {
return j - i;
}
void setV(int p) {
i = p;
}
int j = 0;
};
typedef unordered_map<int, unique_ptr<Base>> BaseMap;
class A {
public:
A(vector<int> keys) {
for (auto& k : keys) {
m.emplace(k, make_unique<Base>(k));
}
}
class Converter {
public:
explicit Converter() {}
pair<BaseMap::key_type, reference_wrapper<Base>> operator()(BaseMap::value_type& p) const {
return {p.first, *p.second};
}
};
using MyIterator = boost::transform_iterator<Converter, typename BaseMap::iterator>;
MyIterator begin() {
return MyIterator(m.begin());
}
MyIterator end() {
return MyIterator(m.end());
}
protected:
BaseMap m;
};
class B : public A {
public:
B(vector<int> keys) : A(keys) {
m.clear(); // demo only, since we have to init A
for (auto& k : keys) {
m.emplace(k, make_unique<Derived>(k));
}
}
class Converter {
public:
explicit Converter() {}
pair<BaseMap::key_type, reference_wrapper<Derived>> operator()(BaseMap::value_type& p) const {
return {p.first, dynamic_cast<Derived&>(*p.second)};
}
};
using MyIterator = boost::transform_iterator<Converter, typename BaseMap::iterator>;
MyIterator begin() {
return MyIterator(m.begin());
}
MyIterator end() {
return MyIterator(m.end());
}
};
int main ()
{
A a({1,2,3,4});
B b({1,2,3,4});
for (auto [k, v] : a) {
cout << v.get().getV() << " ";
}
cout << endl;
for (auto [k, v] : b) {
cout << v.get().getV() << " ";
v.get().setV(42);
}
cout << endl << "after setV:\n";
for (auto [k, v] : b) {
cout << v.get().getV() << " ";
}
return 0;
}
这里有几个问题:
- 您正在按值获取
p
,也就是说,您正在复制。但是std::unique_ptr
不可复制。
BaseMap::value_type
已经是 std::pair<const Key, Value>
.
std::make_pair()
衰减参数,您需要将参数包装到 std::reference_wrapper
和 std::ref()
中以传递引用。
class Converter {
public:
std::pair<BaseMap::key_type, Derived&> operator()(BaseMap::value_type& p) const {
return std::make_pair(p.first, std::ref(dynamic_cast<Derived&>(*p.second)));
}
};
之后,基于范围的 for
应该如下所示:
for (auto [k, v] : b) {
std::cout << v.get();
}
注意 decltype(v)
是 Derived&
, Derived
.
(此答案是在编辑问题代码之前写的。)
编辑:我现在有一个可行的解决方案,https://wandbox.org/permlink/joS14MzfmmayNRd3,只是想知道它是否可以改进。
我有一个映射 map<int, unique_ptr<Base>>
,它的值也可以是 unique_ptr<Derived>
类型。我有包装器 类 A 和 B,它们分别在映射中存储 Base 和 Derived(所有 Base 或所有 Derived 实例)。
我想介绍 A 和 B 的映射语义(引用绑定),其工作方式如下:
// a is of type A, all values in map are of type Base
for (auto& [k, v] : a) {
// v should be of type Base&
}
// b is of type B, all values in map are type Derived
for (auto& [k, v] : b) {
// v should be of type Derived&
// can call functions that are in Derived but not in Base
}
我发现 boost 的 transform_iterator 和 iterator_adaptor 可能会有用(如果有办法不用 boost 就更好),但我似乎没有正确使用它们.正确的做法是什么?
#include <memory>
#include <boost/iterator/transform_iterator.hpp>
#include <vector>
#include <unordered_map>
#include <iostream>
using namespace std;
class Base {
public:
Base(int k) : i(k) {}
virtual ~Base() {}
virtual int getV() {
return i;
}
int i = 0;
};
class Derived : public Base {
public:
Derived(int k) : Base(k) {}
int getV() override {
return j - i;
}
void setV(int p) {
i = p;
}
int j = 0;
};
typedef unordered_map<int, unique_ptr<Base>> BaseMap;
class A {
public:
A(vector<int> keys) {
for (auto& k : keys) {
m.emplace(k, make_unique<Base>(k));
}
}
class Converter {
public:
explicit Converter() {}
pair<BaseMap::key_type, reference_wrapper<Base>> operator()(BaseMap::value_type& p) const {
return {p.first, *p.second};
}
};
using MyIterator = boost::transform_iterator<Converter, typename BaseMap::iterator>;
MyIterator begin() {
return MyIterator(m.begin());
}
MyIterator end() {
return MyIterator(m.end());
}
protected:
BaseMap m;
};
class B : public A {
public:
B(vector<int> keys) : A(keys) {
m.clear(); // demo only, since we have to init A
for (auto& k : keys) {
m.emplace(k, make_unique<Derived>(k));
}
}
class Converter {
public:
explicit Converter() {}
pair<BaseMap::key_type, reference_wrapper<Derived>> operator()(BaseMap::value_type& p) const {
return {p.first, dynamic_cast<Derived&>(*p.second)};
}
};
using MyIterator = boost::transform_iterator<Converter, typename BaseMap::iterator>;
MyIterator begin() {
return MyIterator(m.begin());
}
MyIterator end() {
return MyIterator(m.end());
}
};
int main ()
{
A a({1,2,3,4});
B b({1,2,3,4});
for (auto [k, v] : a) {
cout << v.get().getV() << " ";
}
cout << endl;
for (auto [k, v] : b) {
cout << v.get().getV() << " ";
v.get().setV(42);
}
cout << endl << "after setV:\n";
for (auto [k, v] : b) {
cout << v.get().getV() << " ";
}
return 0;
}
这里有几个问题:
- 您正在按值获取
p
,也就是说,您正在复制。但是std::unique_ptr
不可复制。 BaseMap::value_type
已经是std::pair<const Key, Value>
.std::make_pair()
衰减参数,您需要将参数包装到std::reference_wrapper
和std::ref()
中以传递引用。
class Converter {
public:
std::pair<BaseMap::key_type, Derived&> operator()(BaseMap::value_type& p) const {
return std::make_pair(p.first, std::ref(dynamic_cast<Derived&>(*p.second)));
}
};
之后,基于范围的 for
应该如下所示:
for (auto [k, v] : b) {
std::cout << v.get();
}
注意 decltype(v)
是 Derived&
, Derived
.
(此答案是在编辑问题代码之前写的。)