C++ C++;map——自引用迭代器

C++ C++;map——自引用迭代器,c++,iterator,self-reference,C++,Iterator,Self Reference,有没有方法声明一个值类型为自身迭代器的std::map map<string, map<string, (#)>::iterator> myMap; map-myMap; 上面的代码片段不起作用,因为迭代器类型需要知道第二个模板参数,标记为(#)。(那就是它本身) 其目的是避免执行不必要的find操作来访问另一个元素所指向的元素,而不是使用map这样的定义是不可能的,因为值类型和迭代器类型将是相互无限递归的 可以使用一些间接的方法来解决这个问题。甚至可以避免动态分配s

有没有方法声明一个值类型为自身迭代器的
std::map

map<string, map<string, (#)>::iterator> myMap;
map-myMap;
上面的代码片段不起作用,因为迭代器类型需要知道第二个模板参数,标记为
(#)
。(那就是它本身)


其目的是避免执行不必要的
find
操作来访问另一个元素所指向的元素,而不是使用
map

这样的定义是不可能的,因为值类型和迭代器类型将是相互无限递归的

可以使用一些间接的方法来解决这个问题。甚至可以避免动态分配
std::any
,并且
std::map
是未定义的,除非
V
完成

但是解决方案有点棘手,并且依赖于一些合理的假设,但标准中没有规定。请参见实施中的注释。主要技巧是将成员变量类型的定义推迟到包络类的定义之后。这是通过重用原始存储来实现的

首先使用:

int main()
{
    Map map;
    auto [it, _] = map.emplace("first", iter_wrap{});
    map.emplace("maps to first", conv::wrap(it));
    // erase first mapping by only looking
    // up the element that maps to it
    map.erase(conv::it(map.find("maps to first")));
}
定义

struct NoInitTag {} noInitTag;

class iter_wrap
{
public:
    iter_wrap();
    ~iter_wrap();
    iter_wrap(const iter_wrap&);
    iter_wrap(iter_wrap&&);
    const iter_wrap& operator=(const iter_wrap&);
    const iter_wrap& operator=(iter_wrap&&);

private:
    // We rely on assumption that all map iterators have the same size and alignment.
    // Compiler should hopefully warn if our allocation is insufficient.
    using dummy_it = std::map<int, int>::iterator;
    static constexpr auto it_size = sizeof(dummy_it);
    static constexpr auto it_align = alignof(dummy_it);
    alignas(it_align) std::byte store[it_size];

    explicit iter_wrap(NoInitTag){}
    friend struct conv;
};

using Map = std::map<std::string, iter_wrap>;
using It = Map::iterator;

struct conv {
    static constexpr It&
    it(iter_wrap&& wrap) noexcept {
        return *std::launder(reinterpret_cast<It*>(wrap.store));
    }
    static constexpr const It&
    it(const iter_wrap& wrap) noexcept {
        return *std::launder(reinterpret_cast<const It*>(wrap.store));
    }
    template<class It>
    static const iter_wrap
    wrap(It&& it) {
        iter_wrap iw(noInitTag);
        create(iw, std::forward<It>(it));
        return iw;
    }
    template<class... Args>
    static void
    create(iter_wrap& wrap, Args&&... args) {
        new(wrap.store) It(std::forward<Args>(args)...);
    }
    static constexpr void
    destroy(iter_wrap& wrap) {
        it(wrap).~It();
    }
};

iter_wrap::iter_wrap() {
    conv::create(*this);
}
iter_wrap::iter_wrap(const iter_wrap& other) {
    conv::create(*this, conv::it(other));
}
iter_wrap::iter_wrap(iter_wrap&& other) {
    conv::create(*this, std::move(conv::it(other)));
}
const iter_wrap& iter_wrap::operator=(const iter_wrap& other) {
    conv::destroy(*this);
    conv::create(*this, conv::it(other));
    return *this;
}
const iter_wrap& iter_wrap::operator=(iter_wrap&& other) {
    conv::destroy(*this);
    conv::create(*this, std::move(conv::it(other)));
    return *this;

}
iter_wrap::~iter_wrap() {
    conv::destroy(*this);
}
struct NoInitTag{}NoInitTag;
国际热核聚变实验堆
{
公众:
iter_包裹();
~iter_wrap();
iter_包装(const-iter_包装&);
国际热核聚变实验堆(iter_-wrap&);
const iter_wrap&运算符=(const iter_wrap&);
const iter_wrap&operator=(iter_wrap&);
私人:
//我们依赖于所有映射迭代器具有相同大小和对齐方式的假设。
//如果我们的分配不足,编译器应该发出警告。
使用dummy_it=std::map::迭代器;
静态constexpr auto it_size=sizeof(dummy_it);
静态constexpr auto it_align=alignof(dummy_it);
alignas(it_align)std::字节存储[it_size];
显式iter_包裹(NoInitTag){}
friend struct conv;
};
使用Map=std::Map;
使用它=映射::迭代器;
结构转换{
静态constexpr It&
it(iter_包装和包装)不例外{
退货*标准::流槽(重新解释铸造(包装店));
}
静态constexpr const It&
it(const iter_wrap&wrap)不例外{
退货*标准::流槽(重新解释铸造(包装店));
}
模板
静态常数
包装(It&&It){
国际热核聚变实验堆(NOINITAG);
创建(iw,std::forward(it));
返回iw;
}
模板
静态空隙
创建(iter_wrap&wrap,Args&…Args){
新建(wrap.store)It(std::forward(args)…);
}
静态constexpr void
销毁(iter_包装和包装){
它(包裹)~it();
}
};
iter_wrap::iter_wrap(){
conv::create(*这个);
}
iter_wrap::iter_wrap(const iter_wrap&其他){
conv::create(*this,conv::it(other));
}
iter\u wrap::iter\u wrap(iter\u wrap&其他){
conv::create(*this,std::move(conv::it(other));
}
const iter\u wrap和iter\u wrap::operator=(const iter\u wrap和其他){
conv::destroy(*这个);
conv::create(*this,conv::it(other));
归还*这个;
}
const iter\u wrap和iter\u wrap::operator=(iter\u wrap和其他){
conv::destroy(*这个);
conv::create(*this,std::move(conv::it(other));
归还*这个;
}
iter_wrap::~iter_wrap(){
conv::destroy(*这个);
}

旧答案;这假设在遍历存储的映射时避免查找不是一个重要的特性

您试图表示的数据结构似乎是一组键(字符串),其中每个键都映射到该集的另一个键。更简单的表示方法是将这两个方面分开:

using Set = std::set<std::string>;
using Map = std::map<Set::iterator, Set::iterator>;
使用Set=std::Set;
使用Map=std::Map;

请注意,这两个数据结构不会自动保持同步。添加到集合中的元素不会自动具有到另一个元素的映射,从集合中删除的元素会使迭代器悬空到映射。因此,编写一个自定义容器类来强制执行必要的不变量是明智的。

仅通过类型擦除。例如,您可以使用
std::any

std::map<std::string, std::any> myMap;
auto inserted = myMap.emplace("foo", std::any());

// how it can be populated:
inserted.first->second = inserted.first;
using it_type = decltype(myMap.begin());

// how values can be extracted:
auto it = std::any_cast<it_type>(myMap["foo"]);

只是出于好奇,你想用这个达到什么目的?不,这是不可能的。这种映射的迭代器的定义是无限递归的。也许使用一些间接的技巧,这可以有效地模拟。@MichaelJ一般来说,什么(元素相互指向的映射)基本上等同于一个
std::map
,唯一的区别是找到一个值的键字符串是对数而不是常数,我不知道你想做什么,但是我建议考虑使用比<代码>图更复杂的东西是否值得使用<代码> STD::MAP< /代码>?否则,要找到某个元素的“匹配项”,首先必须
在集合中找到它,然后
在映射中找到相应的迭代器。但作为一种折衷办法,会有重复的字符串…@Anakhand取决于您希望执行的操作类型。另一方面,为什么不仅仅是
std::map
?我最初确实使用了
map
,但我正在尝试通过避免不必要的
find
操作(即访问另一个元素指向的元素)来提高性能。但如果对数时间可以简化程序的话,我想它也没那么糟糕…@Anakhand我认为这是可能的(正如我在对问题的评论中所提到的),但它需要一些聪明的东西和大量的工作。“我有空的时候会调查的。”阿纳坎德我写了一篇概念证明。它可能包含bug。
struct Iter;
using Map = std::map<std::string, Iter>;
struct Iter {
    Map::iterator it;
};