2013-09-26 58 views
0

在下面的例子中,我想通過函數foo和bar來移動「a」。爲什麼「pp」的地址在條形函數中發生變化?我不明白爲什麼。我期待它與「tt」在foo中相同,這與「a」是相同的。如何使用移動語義來鏈接函數?

#include <iostream> 
#include <utility> 
struct A 
{ 
    int a_; 
}; 

template<typename T> 
A foo(T &&t) 
{ 
    auto &&tt = std::move(t); 
    tt.a_ -= 3; 
    std::cout << "tt=" << tt.a_ << "\t&tt=" << &tt << "\n"; 
    return tt; 
} 

template<typename T> 
A bar(T &&p) 
{ 
    auto &&pp = std::move(p); 
    pp.a_++; 
    std::cout << "pp=" << pp.a_ << "\t&pp=" << &pp << "\n"; 
    return pp; 
} 

int main() 
{ 
    A a; 
    a.a_ = 12; 
    std::cout << "a=" << a.a_ << "\t&a=" << &a << "\n"; 
    foo(std::move(a)); 
    std::cout << "a=" << a.a_ << "\t&a=" << &a << "\n"; 
    std::cout << "function chain=" << bar(std::move(foo(std::move(a)))).a_ << "\n"; 
    std::cout << "a=" << a.a_ << "\t&a=" << &a << "\n"; 
    return 0; 
} 

回答

1

幾點:

  • 移動對象仍然意味着存在具有不同的地址不同的實例。如果一個實例被移動,這意味着它的內容以一種理想的更有效的方式從一個實例移動到另一個實例,而不僅僅是複製內容。在你的例子中,沒有任何東西真的被移動,但我認爲你知道這一點。

  • 您從foobar返回A的新實例。這是創建新實例的地方。你打電話bar(std::move(foo(std::move(a))))和內部foo(std::move(a))返回一個新的臨時。然後這個臨時傳遞給bar

  • 請注意,bar(std::move(foo(std::move(a))))是不必要的長,bar(foo(std::move(a)))就足夠因爲臨時已經rvalues。

1

如果你認爲一個變量作爲一個水桶,幷包含一個水桶內的值作爲內容的,那麼從一個變量移動到另一個概念是一個排空水桶到另一個。

考慮到這一點,讓我們看看你想要做什麼:

有兩個功能:

A f(A); 
A g(A); 

這些功能都需要一個值,並返回一個值,並且有副作用。

然後,我們有兩個變量:

A x, y; 

和你想要做y = g(f(x))。那就是你想把x的值移到f的參數中,然後把它從f的返回值中移走,然後把它移到g的參數中,然後把它移到y中。

你可以做到以下幾點:

A& f(A& p) { p.do_stuff(); return p; } 
A& g(A& p) { p.do_stuff(); return p; } 

使得無論是參數和返回值類型的引用。這與std::ostream的工作方式相同。

然後你可以使用對最終結果的舉動將其移動到Y:

A x = ...; 
A y = std::move(f(g(x)); 

這使得x鬥「空」和f和g,在γ處理後的值;

請考慮是否需要兩個變量。也許你可以重用一個:

A x = ...; 
f(g(x)); 

這裏x已被f和g原位突變。實際上並沒有使用移動語義。

您還應該熟悉副本elision,也稱爲RVO和NRVO,它允許某些變量在某些情況下共享相同的地址(是同一個存儲桶)。

相關問題