Making Your Next Move

C++移动语义优化
本文探讨了C++中利用移动语义优化值类型的方法,包括如何通过移动语义提高效率,特别是在二元运算符中的应用。同时介绍了移动专用类型如`std::unique_ptr`的使用,以及如何在不支持移动语义的环境中实现类似效果。

This is the third article in a series about efficient value types in C++. In the previous installment, we introduced C++0x rvalue references, described how to build a movable type, and showed how to explicitly take advantage of that movability. Now we’ll look at another opportunity for move optimization and explore some new areas of the move landscape.

Resurrecting an Rvalue

Before we can discuss our next optimization, you need to know that an unnamed rvalue reference is an rvalue, but a named rvalue reference is an lvalue. I’ll write that again so you can let it sink in:

Important

A named rvalue reference is an lvalue

I realize that’s counterintuitive, but consider the following:

int g(X const&);  // logically non-mutating
int g(X&&);       // ditto, but moves from rvalues
 
int f(X&& a)
{
    g(a);
    g(a);
}

if a were treated as an rvalue inside f, the first call to g would move from a, and the second would see a modifieda. That is not just counter-intuitive; it violates the guarantee that calling g doesn’t visibly modify anything. So a named rvalue reference is just like any other reference, and only unnamed rvalue references are treated specially. To give the second call to g a chance to move, we’d have to rewrite f as follows:

#include <utility> // for std::move
int f(X&& a)
{
    g(a);
    g( std::move(a) );
}

Recall that std::move doesn’t itself do any moving. It merely converts its argument into an unnamed rvalue reference so that move optimizations can kick in.

Binary Operators

Move semantics can be especially great for optimizing the use of binary operators. Consider the following code:

class Matrix
{
     …
     std::vector<double> storage;
};
 
Matrix operator+(Matrix const& left, Matrix const& right)
{
    Matrix result(left);
    result += right;   // delegates to +=
    return result;
}
Matrix a, b, c, d;
…
Matrix x = a + b + c + d;

The Matrix copy constructor gets invoked every time operator+ is called, to create result. Therefore, even if RVO elides the copy of result when it is returned, the expression above makes three Matrix copies (one for each + in the expression), each of which constructs a large vector. Copy elision allows one of these result matrices to be the same object as x, but the other two will need to be destroyed, which adds further expense.

Now, it is possible to write operator+ so that it does better on our expression, even in C++03:

// Guess that the first argument is more likely to be an rvalue
Matrix operator+(Matrix x, Matrix const& y)
{
    x += y;        // x was passed by value, so steal its vector
    Matrix temp;   // Compiler cannot RVO x, so
    swap(x, temp); // make a new Matrix and swap
    return temp;
}
Matrix x = a + b + c + d;

A compiler that elides copies wherever possible will do a near-optimal job with that implementation, making only one temporary and moving its contents directly into x. However, aside from being ugly, it’s easy to foil our optimization:

Matrix x = a + (b + (c + d));

This is actually worse than we’d have done with a naive implementation: now the rvalues always appear on the right-hand side of the + operator, and are copied explicitly. Lvalues always appear on the left-hand side, but are passed by value, and thus are copied implicitly with no hope of elision, so we make six expensive copies.

With rvalue references, though, we can do a reliably optimal1 job by adding overloads to the original implementation:

// The "usual implementation"
Matrix operator+(Matrix const& x, Matrix const& y)
{ Matrix temp = x; temp += y; return temp; }
 
// --- Handle rvalues ---
 
Matrix operator+(Matrix&& temp, const Matrix& y)
{ temp += y; return std::move(temp); }
 
Matrix operator+(const Matrix& x, Matrix&& temp)
{ temp += x; return std::move(temp); }
 
Matrix operator+(Matrix&& temp, Matrix&& y)
{ temp += y; return std::move(temp); }

Move-Only Types

Some types really shouldn’t be copied, but passing them by value, returning them from functions, and storing them in containers makes perfect sense. One example you might be familiar with is std::auto_ptr<T>: you can invoke its copy constructor, but that doesn’t produce a copy. Instead… it moves! Now, moving from an lvalue with copy syntax is even worse for equational reasoning than reference semantics is. What would it mean to sort a container of auto_ptrs if copying a value out of the container altered the original sequence?

Because of these issues, the original standard explicitly outlawed the use of auto_ptr in standard containers, and it has been deprecated in C++0x. Instead, we have a new type of smart pointer that can’t be copied, but can still move:

template <class T>
struct unique_ptr
{
 private:
    unique_ptr(const unique_ptr& p);
    unique_ptr& operator=(const unique_ptr& p);
 public:
    unique_ptr(unique_ptr&& p)
      : ptr_(p.ptr_) { p.ptr_ = 0; }
 
    unique_ptr& operator=(unique_ptr&& p)
    {
        delete ptr_; ptr_ = p.ptr_;
        p.ptr_ = 0;
        return *this;
    }
private: 
    T* ptr_;
};

unique_ptr can be placed in a standard container and can do all the things auto_ptr can do, except implicitly move from an lvalue. If you want to move from an lvalue, you simply pass it through std::move:

int f(std::unique_ptr<T>);    // accepts a move-only type by value
unique_ptr<T> x;              // has a name so it's an lvalue
int a = f( x );               // error! (requires a copy of x)
int b = f( std::move(x) );    // OK, explicit move

Other types that will be move-only in C++0x include stream types, threads and locks (from new mulithreading support), and any standard container holding move-only types.

C++Next Up

There’s still lots to cover. Among other topics in this series, we’ll touch on exception safety, move assignment (again), perfect forwarding, and how to move in C++03. Stay tuned!


Please follow this link to the next installment.


  1. Technically, you can do still better with expression templates, by delaying evaluation of the whole expression until assignment and adding all the matrices “in parallel,” making only one pass over the result. It would be interesting to know if there is a problem that has a truly optimal solution with rvalue references; one that can’t be improved upon by expression templates. 

Value questions/comments:
============================
Howard Hinnant
Why can’t the compiler automatically add std::move to the last use of an lvalue?

Mainly because this would have been dangerous before a very recent change we had to make to the langauge, and since then, no one has implemented, gained experience with, and proposed this change. It takes a lot of time and work to push something through the standardization process.

When and why use std::move in a return statement?

 Use std::move when the argument is not eligible for RVO, and you do want to move from it.

Is a=std::move(a); legal?

 It is advisable to allow self-move assignment in only very limited circumstances. Namely when “a” has already been moved from (is resourceless). It is my hope that the move assignment operator need not go to the trouble or expense of checking for self move assignment:

http://home.roadrunner.com/~hinnant/issue_review/lwg-active.html#1204

I.e. A::operator(A&& a) should be able to assume that “a” really does refer to a temporary.

Marc: Thanks for continuing with these articles. Reading this causes a lot of “why?”.

You’re welcome. I’ve been hoping people would ask some of these questions. While it sounds like you may have the answers all figured out, I’ll write my answers for everyone else’s benefit.

Why can’t the compiler automatically add std::move to the last use of an lvalue?

In general, the answer is that it could break existing code—see the scopeguard example in this posting by Niklas Matthies. If I was considering the design of a new language focused on value semantics (let’s call it V++), I might consider loosening those rules and using an explicit construct for scopeguard-ish things instead, but I would want to be sure not to break cases like this one:

struct X { … };
struct Y
{
    Y(X& x) : x_(x) {}
    ~Y() { do_something_with(x_); }
    X& x_;
};
 
void f()
{
    X a;
    Y b(a);
    …
}

No matter what else happens, Y::~Y() should not encounter an x_ that has been implicitly moved from.

When and why use std::move in a return statement?

When you need to move from an lvalue that doesn’t name a local value with automatic storage duration.

The near optimal version for Matrix with a swap looks highly artificial, why can’t the RVO be cleverer?

The need for the swap is explained here.

Is a=std::move(a); legal?

It’s legal, but not a good idea. Unlike with copy assignment, it’s fairly hard to manufacture a case where a self-move-assignment occurs by mistake, and move assignment is often so fast already that an extra test-and-branch to handle self-assignment can account for a significant fraction of its overall cost, so it’s probably a better practice not to do it. I’ll have more to say about this in the series’ next article.

From
http://cpp-next.com/archive/2009/09/making-your-next-move/#comment-153
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "list.h" /** * @brief 这个是内核链表的一个demo * 1. 第一自己需要的数据类型 ,其中必须包含一个 struct list_head 的变量 2. 定义头节点,并初始化 3. 增加结点,malloc自己的结构体,填入自己需要的数据 调用list_add ,把当前结点加入链表 4. 遍历所有元素list_for_each_entry_safe, */ typedef struct { int id; char name[50]; struct list_head node; } PER; int add_per(struct list_head *head, int id, char *name) { PER *per = malloc(sizeof(PER)); if (NULL == per) { perror("add_per malloc error\n"); return 1; } per->id = id; strcpy(per->name, name); //头插 // list_add(&per->node, head); list_add_tail(&per->node, head); return 0; } int show(struct list_head *head) { // 遍历所有数据, // pos 当前要访问的PER结构体指针 ,n是pos的下一个指针 , // head 链表的头结点 // member 在自定义的结构体中 结点的变量名 // list_for_each_entry_safe(pos, n, head, member) for PER *tmp; PER *next; list_for_each_entry_safe(tmp, next, head, node) { printf("%d %s\n", tmp->id, tmp->name); } return 0; } /** * @brief * * @param head * @param id 需要删除数据的编号 * @return int */ PER* find_per(struct list_head *head, char *name) { PER *tmp; list_for_each_entry(tmp, head, node) { if (strcmp(tmp->name, name) == 0) { printf("找到节点:id=%d, name=%s\n", tmp->id, tmp->name); return tmp; } } printf("未找到姓名为「%s」的节点\n", name); return NULL; } int modify_per(struct list_head *head, char *old_name, int new_id, char *new_name) { PER *target = find_per(head, old_name); if (target == NULL) { return 1; } target->id = new_id; if (strlen(new_name) >= sizeof(target->name)) { printf("无效名字"); return 2; } strcpy(target->name, new_name); printf("修改成功:原姓名「%s」→ 新id=%d, 新姓名「%s」\n", old_name, new_id, new_name); return 0; } int del_per(struct list_head *head, int id) { PER *tmp; PER *next; list_for_each_entry_safe(tmp, next, head, node) { if (tmp->id == id) { list_del(&tmp->node); free(tmp); } } return 0; } int main(int argc, char **argv) { //头结点,不包含有效数据,head->next 是链表中第一个有效数据 struct list_head head; //双向循环链表, 当前结点的prev,next 都指向自己 INIT_LIST_HEAD(&head); add_per(&head, 1, "zhagnsan"); add_per(&head, 2, "lisi"); add_per(&head, 3, "wangmazi"); add_per(&head, 4, "guanerge"); add_per(&head, 5, "liubei "); show(&head); del_per(&head, 1); printf("------------del--------------\n"); show(&head); find_per(&head, "lisi"); // system("pause"); return 0; } #ifndef _LINUX_LIST_H #define _LINUX_LIST_H #include <linux/stddef.h> #include <stdio.h> //#include <linux/poison.h> //#include <linux/prefetch.h> //#include <asm/system.h> /* * Simple doubly linked list implementation. * * Some of the internal functions ("__xxx") are useful when * manipulating whole lists rather than single entries, as * sometimes we already know the next/prev entries and we can * generate better code by using them directly rather than * using the generic single-entry routines. */ // #define LIST_POISON1 ((void *) 0x00100100) // #define LIST_POISON2 ((void *) 0x00200200) #define LIST_POISON1 ((void *) 0) #define LIST_POISON2 ((void *) 0) #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) struct list_head { struct list_head *next, *prev; }; #define LIST_HEAD_INIT(name) \ { \ &(name), &(name) \ } #define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT (name) static inline void INIT_LIST_HEAD (struct list_head *list) { list->next = list; list->prev = list; } /* * Insert a new entry between two known consecutive entries. * * This is only for internal list manipulation where we know * the prev/next entries already! */ #ifndef CONFIG_DEBUG_LIST static inline void __list_add (struct list_head *new, struct list_head *prev, struct list_head *next) { next->prev = new; new->next = next; new->prev = prev; prev->next = new; } #else extern void __list_add (struct list_head *new, struct list_head *prev, struct list_head *next); #endif /** * list_add - add a new entry * @new: new entry to be added * @head: list head to add it after * * Insert a new entry after the specified head. * This is good for implementing stacks. */ static inline void list_add (struct list_head *new, struct list_head *head) { __list_add (new, head, head->next); } /** * list_add_tail - add a new entry * @new: new entry to be added * @head: list head to add it before * * Insert a new entry before the specified head. * This is useful for implementing queues. */ static inline void list_add_tail (struct list_head *new, struct list_head *head) { __list_add (new, head->prev, head); } /* * Delete a list entry by making the prev/next entries * point to each other. * * This is only for internal list manipulation where we know * the prev/next entries already! */ static inline void __list_del (struct list_head *prev, struct list_head *next) { next->prev = prev; prev->next = next; } /** * list_del - deletes entry from list. * @entry: the element to delete from the list. * Note: list_empty() on entry does not return true after this, the entry is * in an undefined state. */ #ifndef CONFIG_DEBUG_LIST static inline void list_del (struct list_head *entry) { __list_del (entry->prev, entry->next); entry->next = LIST_POISON1; entry->prev = LIST_POISON2; } #else extern void list_del (struct list_head *entry); #endif /** * list_replace - replace old entry by new one * @old : the element to be replaced * @new : the new element to insert * * If @old was empty, it will be overwritten. */ static inline void list_replace (struct list_head *old, struct list_head *new) { new->next = old->next; new->next->prev = new; new->prev = old->prev; new->prev->next = new; } static inline void list_replace_init (struct list_head *old, struct list_head *new) { list_replace (old, new); INIT_LIST_HEAD (old); } /** * list_del_init - deletes entry from list and reinitialize it. * @entry: the element to delete from the list. */ static inline void list_del_init (struct list_head *entry) { __list_del (entry->prev, entry->next); INIT_LIST_HEAD (entry); } /** * list_move - delete from one list and add as another's head * @list: the entry to move * @head: the head that will precede our entry */ static inline void list_move (struct list_head *list, struct list_head *head) { __list_del (list->prev, list->next); list_add (list, head); } /** * list_move_tail - delete from one list and add as another's tail * @list: the entry to move * @head: the head that will follow our entry */ static inline void list_move_tail (struct list_head *list, struct list_head *head) { __list_del (list->prev, list->next); list_add_tail (list, head); } /** * list_is_last - tests whether @list is the last entry in list @head * @list: the entry to test * @head: the head of the list */ static inline int list_is_last (const struct list_head *list, const struct list_head *head) { return list->next == head; } /** * list_empty - tests whether a list is empty * @head: the list to test. */ static inline int list_empty (const struct list_head *head) { return head->next == head; } /** * list_empty_careful - tests whether a list is empty and not being modified * @head: the list to test * * Description: * tests whether a list is empty _and_ checks that no other CPU might be * in the process of modifying either member (next or prev) * * NOTE: using list_empty_careful() without synchronization * can only be safe if the only activity that can happen * to the list entry is list_del_init(). Eg. it cannot be used * if another CPU could re-list_add() it. */ static inline int list_empty_careful (const struct list_head *head) { struct list_head *next = head->next; return (next == head) && (next == head->prev); } /** * list_is_singular - tests whether a list has just one entry. * @head: the list to test. */ static inline int list_is_singular (const struct list_head *head) { return !list_empty (head) && (head->next == head->prev); } static inline void __list_cut_position (struct list_head *list, struct list_head *head, struct list_head *entry) { struct list_head *new_first = entry->next; list->next = head->next; list->next->prev = list; list->prev = entry; entry->next = list; head->next = new_first; new_first->prev = head; } /** * list_cut_position - cut a list into two * @list: a new list to add all removed entries * @head: a list with entries * @entry: an entry within head, could be the head itself * and if so we won't cut the list * * This helper moves the initial part of @head, up to and * including @entry, from @head to @list. You should * pass on @entry an element you know is on @head. @list * should be an empty list or a list you do not care about * losing its data. * */ static inline void list_cut_position (struct list_head *list, struct list_head *head, struct list_head *entry) { if (list_empty (head)) return; if (list_is_singular (head) && (head->next != entry && head != entry)) return; if (entry == head) INIT_LIST_HEAD (list); else __list_cut_position (list, head, entry); } static inline void __list_splice (const struct list_head *list, struct list_head *prev, struct list_head *next) { struct list_head *first = list->next; struct list_head *last = list->prev; first->prev = prev; prev->next = first; last->next = next; next->prev = last; } /** * list_splice - join two lists, this is designed for stacks * @list: the new list to add. * @head: the place to add it in the first list. */ static inline void list_splice (const struct list_head *list, struct list_head *head) { if (!list_empty (list)) __list_splice (list, head, head->next); } /** * list_splice_tail - join two lists, each list being a queue * @list: the new list to add. * @head: the place to add it in the first list. */ static inline void list_splice_tail (struct list_head *list, struct list_head *head) { if (!list_empty (list)) __list_splice (list, head->prev, head); } /** * list_splice_init - join two lists and reinitialise the emptied list. * @list: the new list to add. * @head: the place to add it in the first list. * * The list at @list is reinitialised */ static inline void list_splice_init (struct list_head *list, struct list_head *head) { if (!list_empty (list)) { __list_splice (list, head, head->next); INIT_LIST_HEAD (list); } } /** * list_splice_tail_init - join two lists and reinitialise the emptied list * @list: the new list to add. * @head: the place to add it in the first list. * * Each of the lists is a queue. * The list at @list is reinitialised */ static inline void list_splice_tail_init (struct list_head *list, struct list_head *head) { if (!list_empty (list)) { __list_splice (list, head->prev, head); INIT_LIST_HEAD (list); } } /** * list_entry - get the struct for this entry * @ptr: the &struct list_head pointer. * @type: the type of the struct this is embedded in. * @member: the name of the list_struct within the struct. */ #define list_entry(ptr, type, member) container_of (ptr, type, member) /** * list_first_entry - get the first element from a list * @ptr: the list head to take the element from. * @type: the type of the struct this is embedded in. * @member: the name of the list_struct within the struct. * * Note, that list is expected to be not empty. */ #define list_first_entry(ptr, type, member) \ list_entry ((ptr)->next, type, member) /** * list_for_each - iterate over a list * @pos: the &struct list_head to use as a loop cursor. * @head: the head for your list. */ #define list_for_each(pos, head) \ for (pos = (head)->next; prefetch (pos->next), pos != (head); \ pos = pos->next) /** * __list_for_each - iterate over a list * @pos: the &struct list_head to use as a loop cursor. * @head: the head for your list. * * This variant differs from list_for_each() in that it's the * simplest possible list iteration code, no prefetching is done. * Use this for code that knows the list to be very short (empty * or 1 entry) most of the time. */ #define __list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next) /** * list_for_each_prev - iterate over a list backwards * @pos: the &struct list_head to use as a loop cursor. * @head: the head for your list. */ #define list_for_each_prev(pos, head) \ for (pos = (head)->prev; prefetch (pos->prev), pos != (head); \ pos = pos->prev) /** * list_for_each_safe - iterate over a list safe against removal of list entry * @pos: the &struct list_head to use as a loop cursor. * @n: another &struct list_head to use as temporary storage * @head: the head for your list. */ #define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next) /** * list_for_each_prev_safe - iterate over a list backwards safe against removal * of list entry * @pos: the &struct list_head to use as a loop cursor. * @n: another &struct list_head to use as temporary storage * @head: the head for your list. */ #define list_for_each_prev_safe(pos, n, head) \ for (pos = (head)->prev, n = pos->prev; \ prefetch (pos->prev), pos != (head); pos = n, n = pos->prev) /** * list_for_each_entry - iterate over list of given type * @pos: the type * to use as a loop cursor. * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry(pos, head, member) \ for (pos = list_entry ((head)->next, typeof(*pos), member); \ prefetch (pos->member.next), &pos->member != (head); \ pos = list_entry (pos->member.next, typeof(*pos), member)) /** * list_for_each_entry_reverse - iterate backwards over list of given type. * @pos: the type * to use as a loop cursor. * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry_reverse(pos, head, member) \ for (pos = list_entry ((head)->prev, typeof(*pos), member); \ prefetch (pos->member.prev), &pos->member != (head); \ pos = list_entry (pos->member.prev, typeof(*pos), member)) /** * list_prepare_entry - prepare a pos entry for use in * list_for_each_entry_continue() * @pos: the type * to use as a start point * @head: the head of the list * @member: the name of the list_struct within the struct. * * Prepares a pos entry for use as a start point in * list_for_each_entry_continue(). */ #define list_prepare_entry(pos, head, member) \ ((pos) ?: list_entry (head, typeof(*pos), member)) /** * list_for_each_entry_continue - continue iteration over list of given type * @pos: the type * to use as a loop cursor. * @head: the head for your list. * @member: the name of the list_struct within the struct. * * Continue to iterate over list of given type, continuing after * the current position. */ #define list_for_each_entry_continue(pos, head, member) \ for (pos = list_entry (pos->member.next, typeof(*pos), member); \ prefetch (pos->member.next), &pos->member != (head); \ pos = list_entry (pos->member.next, typeof(*pos), member)) /** * list_for_each_entry_continue_reverse - iterate backwards from the given * point * @pos: the type * to use as a loop cursor. * @head: the head for your list. * @member: the name of the list_struct within the struct. * * Start to iterate over list of given type backwards, continuing after * the current position. */ #define list_for_each_entry_continue_reverse(pos, head, member) \ for (pos = list_entry (pos->member.prev, typeof(*pos), member); \ prefetch (pos->member.prev), &pos->member != (head); \ pos = list_entry (pos->member.prev, typeof(*pos), member)) /** * list_for_each_entry_from - iterate over list of given type from the current * point * @pos: the type * to use as a loop cursor. * @head: the head for your list. * @member: the name of the list_struct within the struct. * * Iterate over list of given type, continuing from current position. */ #define list_for_each_entry_from(pos, head, member) \ for (; prefetch (pos->member.next), &pos->member != (head); \ pos = list_entry (pos->member.next, typeof(*pos), member)) /** * list_for_each_entry_safe - iterate over list of given type safe against * removal of list entry * @pos: the type * to use as a loop cursor. * @n: another type * to use as temporary storage * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry_safe(pos, n, head, member) \ for (pos = list_entry ((head)->next, typeof(*pos), member), \ n = list_entry (pos->member.next, typeof(*pos), member); \ &pos->member != (head); \ pos = n, n = list_entry (n->member.next, typeof(*n), member)) /** * list_for_each_entry_safe_continue * @pos: the type * to use as a loop cursor. * @n: another type * to use as temporary storage * @head: the head for your list. * @member: the name of the list_struct within the struct. * * Iterate over list of given type, continuing after current point, * safe against removal of list entry. */ #define list_for_each_entry_safe_continue(pos, n, head, member) \ for (pos = list_entry (pos->member.next, typeof(*pos), member), \ n = list_entry (pos->member.next, typeof(*pos), member); \ &pos->member != (head); \ pos = n, n = list_entry (n->member.next, typeof(*n), member)) /** * list_for_each_entry_safe_from * @pos: the type * to use as a loop cursor. * @n: another type * to use as temporary storage * @head: the head for your list. * @member: the name of the list_struct within the struct. * * Iterate over list of given type from current point, safe against * removal of list entry. */ #define list_for_each_entry_safe_from(pos, n, head, member) \ for (n = list_entry (pos->member.next, typeof(*pos), member); \ &pos->member != (head); \ pos = n, n = list_entry (n->member.next, typeof(*n), member)) /** * list_for_each_entry_safe_reverse * @pos: the type * to use as a loop cursor. * @n: another type * to use as temporary storage * @head: the head for your list. * @member: the name of the list_struct within the struct. * * Iterate backwards over list of given type, safe against removal * of list entry. */ #define list_for_each_entry_safe_reverse(pos, n, head, member) \ for (pos = list_entry ((head)->prev, typeof(*pos), member), \ n = list_entry (pos->member.prev, typeof(*pos), member); \ &pos->member != (head); \ pos = n, n = list_entry (n->member.prev, typeof(*n), member)) /* * Double linked lists with a single pointer list head. * Mostly useful for hash tables where the two pointer list head is * too wasteful. * You lose the ability to access the tail in O(1). */ struct hlist_head { struct hlist_node *first; }; struct hlist_node { struct hlist_node *next, **pprev; }; #define HLIST_HEAD_INIT \ { \ .first = NULL \ } #define HLIST_HEAD(name) struct hlist_head name = { .first = NULL } #define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL) static inline void INIT_HLIST_NODE (struct hlist_node *h) { h->next = NULL; h->pprev = NULL; } static inline int hlist_unhashed (const struct hlist_node *h) { return !h->pprev; } static inline int hlist_empty (const struct hlist_head *h) { return !h->first; } static inline void __hlist_del (struct hlist_node *n) { struct hlist_node *next = n->next; struct hlist_node **pprev = n->pprev; *pprev = next; if (next) next->pprev = pprev; } static inline void hlist_del (struct hlist_node *n) { __hlist_del (n); n->next = LIST_POISON1; n->pprev = LIST_POISON2; } static inline void hlist_del_init (struct hlist_node *n) { if (!hlist_unhashed (n)) { __hlist_del (n); INIT_HLIST_NODE (n); } } static inline void hlist_add_head (struct hlist_node *n, struct hlist_head *h) { struct hlist_node *first = h->first; n->next = first; if (first) first->pprev = &n->next; h->first = n; n->pprev = &h->first; } /* next must be != NULL */ static inline void hlist_add_before (struct hlist_node *n, struct hlist_node *next) { n->pprev = next->pprev; n->next = next; next->pprev = &n->next; *(n->pprev) = n; } static inline void hlist_add_after (struct hlist_node *n, struct hlist_node *next) { next->next = n->next; n->next = next; next->pprev = &n->next; if (next->next) next->next->pprev = &next->next; } /* * Move a list from one list head to another. Fixup the pprev * reference of the first entry if it exists. */ static inline void hlist_move_list (struct hlist_head *old, struct hlist_head *new) { new->first = old->first; if (new->first) new->first->pprev = &new->first; old->first = NULL; } #define hlist_entry(ptr, type, member) container_of (ptr, type, member) #define hlist_for_each(pos, head) \ for (pos = (head)->first; pos && ({ \ prefetch (pos->next); \ 1; \ }); \ pos = pos->next) #define hlist_for_each_safe(pos, n, head) \ for (pos = (head)->first; pos && ({ \ n = pos->next; \ 1; \ }); \ pos = n) /** * hlist_for_each_entry - iterate over list of given type * @tpos: the type * to use as a loop cursor. * @pos: the &struct hlist_node to use as a loop cursor. * @head: the head for your list. * @member: the name of the hlist_node within the struct. */ #define hlist_for_each_entry(tpos, pos, head, member) \ for (pos = (head)->first; \ pos && ({ \ prefetch (pos->next); \ 1; \ }) \ && ({ \ tpos = hlist_entry (pos, typeof(*tpos), member); \ 1; \ }); \ pos = pos->next) /** * hlist_for_each_entry_continue - iterate over a hlist continuing after * current point * @tpos: the type * to use as a loop cursor. * @pos: the &struct hlist_node to use as a loop cursor. * @member: the name of the hlist_node within the struct. */ #define hlist_for_each_entry_continue(tpos, pos, member) \ for (pos = (pos)->next; \ pos && ({ \ prefetch (pos->next); \ 1; \ }) \ && ({ \ tpos = hlist_entry (pos, typeof(*tpos), member); \ 1; \ }); \ pos = pos->next) /** * hlist_for_each_entry_from - iterate over a hlist continuing from current * point * @tpos: the type * to use as a loop cursor. * @pos: the &struct hlist_node to use as a loop cursor. * @member: the name of the hlist_node within the struct. */ #define hlist_for_each_entry_from(tpos, pos, member) \ for (; pos && ({ \ prefetch (pos->next); \ 1; \ }) \ && ({ \ tpos = hlist_entry (pos, typeof(*tpos), member); \ 1; \ }); \ pos = pos->next) /** * hlist_for_each_entry_safe - iterate over list of given type safe against * removal of list entry * @tpos: the type * to use as a loop cursor. * @pos: the &struct hlist_node to use as a loop cursor. * @n: another &struct hlist_node to use as temporary storage * @head: the head for your list. * @member: the name of the hlist_node within the struct. */ #define hlist_for_each_entry_safe(tpos, pos, n, head, member) \ for (pos = (head)->first; \ pos && ({ \ n = pos->next; \ 1; \ }) \ && ({ \ tpos = hlist_entry (pos, typeof(*tpos), member); \ 1; \ }); \ pos = n) #endif 错误信息为In file included from main.c:4:0: main.c: In function ‘find_per’: list.h:417:8: warning: implicit declaration of function ‘prefetch’; did you mean ‘rpmatch’? [-Wimplicit-function-declaration] prefetch (pos->member.next), &pos->member != (head); \ ^ main.c:63:5: note: in expansion of macro ‘list_for_each_entry’ list_for_each_entry(tmp, head, node) ^~~~~~~~~~~~~~~~~~~ /tmp/cce8BVSj.o: In function `find_per': main.c:(.text+0x297): undefined reference to `prefetch' collect2: error: ld returned 1 exit status,该如何修改
08-24
基于数据驱动的 Koopman 算子的递归神经网络模型线性化,用于纳米定位系统的预测控制研究(Matlab代码实现)内容概要:本文围绕“基于数据驱动的Koopman算子的递归神经网络模型线性化”展开,旨在研究纳米定位系统的预测控制问题,并提供完整的Matlab代码实现。文章结合数据驱动方法与Koopman算子理论,利用递归神经网络(RNN)对非线性系统进行建模与线性化处理,从而提升纳米级定位系统的精度与动态响应性能。该方法通过提取系统隐含动态特征,构建近似线性模型,便于后续模型预测控制(MPC)的设计与优化,适用于高精度自动化控制场景。文中还展示了相关实验验证与仿真结果,证明了该方法的有效性和先进性。; 适合人群:具备一定控制理论基础和Matlab编程能力,从事精密控制、智能制造、自动化或相关领域研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①应用于纳米级精密定位系统(如原子力显微镜、半导体制造设备)中的高性能控制设计;②为非线性系统建模与线性化提供一种结合深度学习与现代控制理论的新思路;③帮助读者掌握Koopman算子、RNN建模与模型预测控制的综合应用。; 阅读建议:建议读者结合提供的Matlab代码逐段理解算法实现流程,重点关注数据预处理、RNN结构设计、Koopman观测矩阵构建及MPC控制器集成等关键环节,并可通过更换实际系统数据进行迁移验证,深化对方法泛化能力的理解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值