- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
几年来我第三次发现自己需要一个侵入式链表来处理一个不允许提升的项目(询问管理层...)。
第三次我发现我拥有的侵入式链表实现完美运行,但我真的不喜欢它使用未定义的行为 - 即将指向列表节点的指针转换为指向包含该列表节点的对象。
那个糟糕的代码目前看起来像这样:
struct IntrusiveListNode {
IntrusiveListNode * next_;
IntrusiveListNode * prev_;
};
template <typename T, IntrusiveListNode T::*member>
class IntrusiveList {
// snip ...
private:
T & nodeToItem_(IntrusiveListNode & node) {
return *(T*)(((char*)&node)-((size_t)&(((T*)nullptr)->*member)));
}
IntrusiveListNode root_;
};
我不在乎有多丑 nodeToItem_
得到,但我想保留 IntrusiveList
的公共(public)接口(interface)和语法相同。具体来说,我想使用 IntrusiveList<Test, &Test::node_>
指定列表类型的类型。而不是 IntrusiveList<Test, offsetof(Test, node_)>
.
快到 2016 年了 - 有没有办法在不调用未定义行为的情况下做到这一点?
编辑:我想在这里总结的评论中有一些建议的解决方案(涉及列表的不同结构):
存在未定义的行为,因为该语言似乎具有任意限制,阻止反向使用成员指针。
在 IntrusiveListNode
中存储一个指向包含类的附加指针.这可能是目前最干净的解决方案(无需更改接口(interface)),但确实需要在每个列表节点中添加第三个指针(可能进行小的优化)。
源自 IntrusiveListNode
并使用 static_cast
.在提升中,这是 base_hook
侵入式链表的版本。我想坚持 member_hook
版本以避免引入多重继承。
存储指向下一个和上一个包含类的指针,而不是指向 IntrusiveListNode
内的下一个和上一个列表节点的指针.这使得在侵入列表中创建根节点变得困难。列表必须包含 T
的完整实例化。 (这是不可能的,例如如果 T
是抽象的),或者列表的末尾需要是一个空指针(这会破坏 --list.end()
,只允许向前迭代)。
Boost 侵入式列表有 member_hook
以某种方式工作的版本,但尚未理解实现(并且它可能还依赖于未定义的行为)。
问题仍然存在:是否可以创建一个具有双向迭代支持、没有未定义行为和“不必要”内存开销的侵入式基于成员的列表?
最佳答案
我会回避这个问题并使用 node<T>
含有合适的成员链接范围。应对双向、侵入性列表我会使用不对称的 node<T>
像这样:
template <typename T>
class intrusive::node
{
template <typename S, node<S> S::*> friend class intrusive::list;
template <typename S, node<S> S::*> friend class intrusive::iterator;
T* next;
node<T>* prev;
public:
node(): next(), prev() {}
node(node const&) {}
void operator=(node const&) {}
};
基本思想是list<T, L>
包含 node<T>
使用next
指向第一个元素的指针。这是相当的直截了当:给定一个指针 p
到 T
下一个链接可以使用 (p->*L).next
遍历节点.然而,而不是使用 T*
直接导航列表, 一个 iterator<T, L>
实际上使用指向 node<T>
的指针:虽然这不是必需的前向遍历,它支持后向遍历和插入列表中的任何位置,无需对列表头进行特殊处理。
复制构造函数和复制赋值被定义为什么都不做在复制节点时避免半插入节点。取决于节点的需求可能更合理= delete
这些操作。但是,这与手头的问题无关。
迭代器只使用指向 node<T>
的指针。谁的next
当前节点的成员点。对于第一个元素列出这是指向 list<T, L>
的指针的node<T>
成员。假设您有一个指向合适的 node<T>
的指针一个 iterator<T,
可以从中创建:
L>
template <typename T, intrusive::node<T> T::*Link>
class intrusive::iterator
{
template <typename S, node<S> S::*> friend class intrusive::list;
node<T>* current;
public:
explicit iterator(node<T>* current): current(current) {}
T& operator*() { return *this->operator->(); }
T* operator->() { return this->current->next; }
bool operator== (iterator const& other) const {
return this->current == other.current;
}
bool operator!= (iterator const& other) const {
return !(*this == other);
}
iterator& operator++() {
this->current = &(this->current->next->*Link);
return *this;
}
iterator operator++(int) {
iterator rc(*this);
this->operator++();
return rc;
}
iterator& operator--() {
this->current = this->current->prev;
return *this;
}
iterator operator--(int) {
iterator rc(*this);
this->operator--();
return rc;
}
};
取消引用只使用 next
指针。对于使用 next
的前向迭代指针与获取下一个 node<T>
的地址的成员指针.由于迭代器的 prev
已经指向 node<T>
落后迭代只需要替换当前的node<T>
与 prev
元素。
最后,这留下了一个维护开头和结尾的列表的名单。处理双向访问和相应的访问最后一个节点会增加一些复杂性,并且需要实际上有一个专用节点。这是一个实现(其中没有经过彻底测试:我可能弄乱了一些链接):
template <typename T, intrusive::node<T> T::*Link>
class intrusive::list
{
node<T> content;
public:
list() { this->content.prev = &this->content; }
iterator<T, Link> begin() { return iterator<T, Link>(&this->content); }
iterator<T, Link> end() { return iterator<T, Link>(this->content.prev); }
T& front() { return *this->content.next; }
T& back() { return *(this->content.prev->prev->next); }
bool empty() const { return &this->content == this->content.prev; }
void push_back(T& node) { this->insert(this->end(), node); }
void push_front(T& node) { this->insert(this->begin(), node); }
void insert(iterator<T, Link> pos, T& node) {
(node.*Link).next = pos.current->next;
((node.*Link).next
? (pos.current->next->*Link).prev
: this->content.prev) = &(node.*Link);
(node.*Link).prev = pos.current;
pos.current->next = &node;
}
iterator<T, Link> erase(iterator<T, Link> it) {
it.current->next = (it.current->next->*Link).next;
(it.current->next
? (it.current->next->*Link).prev
: this->content.prev) = it.current;
return iterator<T, Link>(&(it.current->next->*Link));
}
};
只是为了有点理智:这是一个简单地打印列表的函数:
template <typename T, intrusive::node<T> T::*Link>
std::ostream& intrusive::operator<< (std::ostream& out, intrusive::list<T, Link>& list)
{
out << "[";
if (!list.empty()) {
std::copy(list.begin(), --list.end(), std::ostream_iterator<T>(out, ", "));
out << list.back();
}
return out << "]";
}
几乎没有其他方法可以避免做任何时髦的事情访问封闭类。以上避免了几个条件。假设我设法设置适当的链接更正代码不会依赖任何实现定义或未定义的行为。
你会像这样使用列表:
class Node {
public:
intrusive::node<Node> link0;
intrusive::node<Node> link1;
int n;
Node(int n): n(n) {}
};
std::ostream& operator<< (std::ostream& out, Node const& n) {
return out << n.n;
}
int main()
{
intrusive::list<Node, &Node::link0> l0;
intrusive::list<Node, &Node::link1> l1;
Node n[] = { 10, 11, 12, 13, 14, 15 };
l0.push_front(n[0]);
l0.push_front(n[1]);
l0.push_front(n[2]);
l1.push_back(n[0]);
l1.push_back(n[1]);
l1.push_back(n[2]);
std::cout << "l0=" << l0 << " l1=" << l1 << "\n";
}
关于c++ - 如何实现避免未定义行为的侵入式链表?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34134886/
注意:根据 ECMAScript5.1, section 15.1.1.3 , window.undefined 是只读的。 现代浏览器正确地实现了这一点。例如:Safari 5.1、Firefox
我用谷歌搜索,检查了所有常见的响应。这些脚本似乎顺序正确(它们在 the demo 中以相同的顺序工作)但由于某种原因,当我尝试实现它时,我得到了错误。 我有一个 paste bin用我完整的页面代码
我有以下代码: simpleExample.html: Simple example Open the Console. js/simpleExampleJS.
所以我对 typescript 很陌生。 这是我的 tsconfig.json 文件的样子, 我什至不知道那些“例子”是从哪里来的 这是我在浏览器中遇到的错误 这是我的 package.json 最佳
我被 TypeScript 错误困住了: Failed to compile. undefined TypeScript error in undefined(undefined,undefined)
我已将 onlcick 事件附加到表单的提交按钮以覆盖默认的 POST 请求,但在使其正常工作时遇到了一些问题。 我想要的是点击添加到购物车的商品,但只显示模式确认而不刷新页面。 这段代码在我的静态示
我已经完成了这个link .但是给定的解决方案并没有解决我的问题。所有经验丰富的人都建议使用 getActivty().getApplicationContext()。但我使用的是普通类(class)
我有一个应用程序,其中 webpack 配置为模块 bundler ,babel 作为我的 js 编译器。全部都在 Node 引擎上。我设置当我运行 npm run dev 时,将会发生以下流程: w
我正在尝试在浏览器中呈现网址的 JSON 数据,但它在 div 中显示为 undefined, undefined .当我将其响应放入console.log时对象及其数据出现,因此它出现在控制台和浏览
为什么我会收到 Cannot invoke an object which is possibly 'undefined'即使在我检查了 func 之后,Typescript 也会出错引用不是未定义的
我想使用airbnb的eslint修改我的代码结构。我已遵循 eslint-config-airbnb 中给出的说明。启动命令后npm run lint,结果由 'fetch' is not Defi
这个问题在这里已经有了答案: What is the consequence of this bit of javascript? (4 个答案) 关闭 9 年前。 我看到一些 jQuery 脚本嵌
我一直收到这个 pouchDB 错误,我不知道这是怎么回事。 Error: connect ENFILE 127.0.0.1:5984 - Local (undefined:undefined)
import os from os.path import abspath, dirname import sys # Set up django project_dir = abspath(dirn
在 javascript 中,要检查变量是否从未创建过,我们只需执行 if (typeof MyVariable !== "undefined"){ ... } 我想知道如何在 CoffeeScrip
我正在尝试编译 FreeLing,它使用 CMake 来检测 Boost。这是负责它的代码: find_package(Boost COMPONENTS regex filesystem thre
我收到“JQUERY 未定义”错误。你有什么想法吗(代码如下)。 $(document).ready(function() { window.print();
无论我是否将 jQuery $.ajax 调用放入 $(document).ready(function() { 中,我都会收到此错误。这是在 ASP.NET MVC .cshtml 文件上.
我正在构建的网站上遇到问题,但数据表不存在加载(在服务器上)但在本地主机(xamp)上工作正常。 Firefox 会抛出错误“$ 未定义”。 我用谷歌搜索了这些错误,我发现一个 friend 的解决方
当我在 $(document).ready 之前明确包含 jQuery 库时,我无法弄清楚为什么它仍然无法识别 jQuery 语法
我是一名优秀的程序员,十分优秀!