2011-11-27 17 views
7

簡單的問題,爲什麼不the following工作(暗示副本ci)?爲什麼不`const int ci = 2; std ::轉發<int>(ci);`工作以及如何修復/解決方法呢?

#include <utility> 

int main(){ 
    const int ci = 2; 
    std::forward<int>(ci); 
} 

問題表現出來,而寫一些東西的模板,在那裏我有一個簡單的支架類型,如下所示。爲了避免不必要的副本,我儘可能使用完美的轉發方式,但事實證明這似乎是問題的根源。

template<class T> 
struct holder{ 
    T value; 

    holder(T&& val) 
     : value(std::forward<T>(val)) 
    {} 
}; 

template<class T> 
holder<T> hold(T&& val){ 
    // T will be deduced as int, because literal `5` is a prvalue 
    // which can be bound to `int&&` 
    return holder<T>(std::forward<T>(val)); 
} 

template<class T> 
void foo(holder<T> const& h) 
{ 
    std::tuple<T> t; // contrived, actual function takes more parameters 
    std::get<0>(t) = std::forward<T>(h.value); // h.value is `const T` 
} 

int main(){ 
    foo(hold(5)); 
} 

如果需要更多信息,請告訴我。
任何想法來規避這個問題是非常感謝。

+0

你應該這樣做:'template struct holder {holder(T val):value(std :: move(val)){}};模板持有人 :: type> hold(T val){return holder (std :: move(val)); }(是的,我知道這看起來很混亂:)) – GManNickG

+0

@GMan:但我不想刪除這個參考。 :( – Xeo

+1

然後你把它叫做'hold(std :: ref(x));':P – GManNickG

回答

15

此:

#include <utility> 

int main(){ 
    const int ci = 2; 
    std::forward<int>(ci); 
} 

不起作用,因爲你不能隱拋棄conststd::forward<T>(u)應該讀作:

Forward u as a T .

您正試圖說:

Forward an lvalue `const int` as an rvalue `int`. 

它扔掉了const。爲了避免丟掉const你可以:

#include <utility> 

int main(){ 
    const int ci = 2; 
    std::forward<const int>(ci); 
} 

它說:

Forward an lvalue `const int` as an rvalue `const int`. 

在您的代碼:

template<class T> 
void foo(holder<T> const& h) 
{ 
    std::tuple<T> t; // contrived, actual function takes more parameters 
    std::get<0>(t) = std::forward<T>(h.value); // h.value is `const T` 
} 

const預選賽上h影響數據成員選擇表達h.valueh.valueconst左值int。您可以使用forward將其更改爲const右值int,或者可以使用forward將其保持不變(作爲const左值int)。你可能甚至使用forward添加volatile(雖然我想不出一個很好的理由)。

在你的例子中,我看不到任何理由使用forward(除非你從關閉h)。

std::get<0>(t) = h.value; // h.value is `const T` 

您的評論甚至是正確的。

這是幹讀,但N2951調查了你可以和不能用forward做什麼,以及爲什麼。在標準化之前,這已被N3143修改,但最終N3143配方中的使用案例和依據仍然有效並保持不變。

的事情你可以做forward

  • 你可以把一個左值作爲左值。
  • 您可以將左值作爲右值轉發。
  • 您可以將右值作爲右值轉發。
  • 您可以將少量cv限定的表達式轉發到更多的cv限定表達式。
  • 您可以將派生類型的表達式轉換爲可訪問的,明確的基本類型。

東西,你可以不forward

  • 您不能轉發右值作爲左值。
  • 您不能將更多的cv限定表達式轉發到更少的cv限定表達式。
  • 您無法轉發任意類型轉換(例如,將int轉發爲double)。
+0

感謝這個廣泛的答案。有什麼方法可以「節省」價值的可轉換性?就像我在對問題本身的評論中所說的那樣,我試圖實現位置參數,並且我希望將所有參數直接傳遞到函數中。 [Here](http://ideone.com/jeprD)是我目前擁有的完整代碼(〜300 LoC),儘管GCC似乎並不喜歡'switch_'上的SFINAE技巧。 – Xeo

+0

我已經在我的系統中發現了一些限制,比如我不能在'param_pack'中將'T'類型的臨時類型綁定到'T const&'或'T &&'而不會引入懸掛引用。有什麼方法可以修復/改進(如果你願意通過代碼)? – Xeo

+0

@Xeo:我不確定。但我能夠修復你的代碼,使它運行在clang/libC++上。 'std :: move(pos :: template switch_ <0>(arg1,arg2,arg3).value'(add「template」3 places)。使param_pack BoundList構造函數不那麼通用:'template param_pack(bound_list && bl)'。這些變化你的代碼,我和產出運行:'價值男星 移動男星 移動男星 移動男星 拷貝構造函數 移動男星 移動男星 喜3 1 析構函數 Dtor' –

相關問題