2015-04-12 59 views
2

我聽不太懂幾個關於完美轉發選項點由香草薩特在他的介紹提出"Back to the Basics! Essentials of Modern C++ Style"(@ 1:15:00)在CppCon 2014 三個相關的幻燈片(here are the slides online )有以下幾種: enter image description here enter image description here enter image description here香草薩特的CppCon完美轉發幻燈片

我認爲在選項#4中的模板的成員函數應啓用如果腐朽類型的String相同std::string而不是不同幻燈片上如所述(否則選項#2和#4不會等價,並且沒有std::string賦值運算符,無論如何都接受非std::string右值)。 但除此之外,我不

已瞭解
  • 還有什麼第4個選項可以從除了竊取右值和
  • 爲什麼,如圖第三幻燈片基準
  • ,第4個選項將是比#2快得多(特別是在最後的基準測試中)。他們不應該做同樣的事嗎?
+0

'std :: string'有一個賦值操作符,它需要'char const *'。如果LHS上的「std :: string」已經有足夠的空間,這個運算符不必分配內存。 – dyp

+0

@dyp也適用於從std :: string複製。另外,#4不能接受字符串文字。 – bmanga

+2

由於參數類型的原因,所有其他選項(但完美轉發)都要求調用者轉換爲'std :: string'。如果參數是一個「char const *」,即使生成的「std :: string」(函數參數)可以被複制,而沒有額外的分配給「name_」,也需要分配內存。至於選項#4,IIRC幻燈片上有一些錯誤。一個完美的轉發函數會使用'std :: is_convertible '。 – dyp

回答

2

「不一樣」是在編寫完美轉換構造函數時使用的模式 - 當傳遞的類型是您自己類型的某種變體時,您不想使用此轉換器。可能是它包括在這裏的複製意大利麪。

真的,你想使用一個特性「這可以分配給一個字符串」:std::enable_if_t<std::is_assignable<std::string, String>::value>>,因爲這是你所關心的。你可以走得更遠,並測試它是否可賦值(如果是這樣,使用它),如果它是可轉換的(如果是,轉換,然後賦值)則失敗,但我不會。

簡言之,條件看起來像來自相關測試的複製麪食。你真的不想限制太多。

至於爲什麼它擊敗了選項#2,如果容器中的std::string已經分配了內存,則它可以從char const*複製而不分配更多內存。如果改爲string&&,則char const*首先轉換爲string,然後移動分配。我們有兩個字符串,一個被丟棄。

你所看到的是內存分配開銷。

完美的轉發不需要分配內存。


現在,爲了完整起見,還有另一種選擇。實施起來有點瘋狂,但它幾乎與選項#4一樣有效,並且缺點很少。

選項5:鍵入擦除分配。 assignment_view<std::string>

寫一個類類型擦除「分配給輸入T」。把它當作你的論點。在裏面使用它。

這比完美的轉發更教導。該方法可以是虛擬的,因爲我們正在採用具體類型(指定給字符串的具體類型)。類型擦除發生在施工人員的施工過程中。爲每個分配的類型生成一些代碼,但代碼僅限於分配,而不是函數的整個主體。

其中,在每一分配一些開銷(類似於虛擬函數調用,主要是成本高昂由於指令高速緩存未命中)。所以這並不完美。

你叫a.assign_to(name)做任務,而不是爲name = a最大效率。如果你喜歡語法,你可以做name << std::move(a);

爲了最大效率,分配刪除視圖(無論你怎麼稱呼它)只能用於生產一個任務:這使得它能夠優化移動語義。你也可以做一個聰明的人,在&&&的基礎上做一些不同的事情,分配一個額外的函數指針開銷。

here I型抹去T == ?概念。這隻需要類型擦除T = ?的概念。 (我可以爲{}初始化一點點用Ts&&...男星到現在的類型擦除對象更好的語法:那是我的第一次嘗試)

live example型擦除到分配std::string

template<class...>struct voider{using type=void;}; 
template<class...Ts>using void_t=typename voider<Ts...>::type; 
template<class T>struct tag{using type=T;}; 

template<class...>struct types{using type=types;}; 

template<class T> 
using block_deduction = typename tag<T>::type; 

template<class F, class Sig, class T=void> 
struct erase_view_op; 

template<class F, class R, class...Ts, class T> 
struct erase_view_op<F, R(Ts...), T> 
{ 
    using fptr = R(*)(void const*, Ts&&...); 

    fptr f; 
    void const* ptr; 

private: 
    template<class U> 
    erase_view_op(U&& u, int): 
    f([](void const* p, Ts&&...ts)->R{ 
     U& u = reinterpret_cast<U&>(*static_cast<std::decay_t<U>*>(const_cast<void*>(p))); 
     return F{}(u, std::forward<Ts>(ts)...); 
    }), 
    ptr(static_cast<void const*>(std::addressof(u))) 
    {} 
public: 
    template<class U, class=std::enable_if_t< !std::is_same<std::decay_t<U>,erase_view_op>{} && (std::is_same<void,R>{} || std::is_convertible< std::result_of_t<F(U,Ts...)>, R >{}) >> 
    erase_view_op(U&& u):erase_view_op(std::forward<U>(u), 0){} 

    template<class U=T, class=std::enable_if_t< !std::is_same<U, void>{} >> 
    erase_view_op(block_deduction<U>&& u):erase_view_op(std::move(u), 0){} 

    erase_view_op(erase_view_op const&) = default; 
    erase_view_op(erase_view_op&&) = default; 

    R operator()(Ts... ts) const { 
    return f(ptr, std::forward<Ts>(ts)...); 
    } 
}; 

struct assign_lhs_to_rhs { 
    template<class lhs, class rhs> 
    void operator()(lhs&& l, rhs& r)const { 
    r = std::forward<lhs>(l); 
    } 
}; 
template<class T> 
using erase_assignment_to = erase_view_op< assign_lhs_to_rhs, void(T&), T >; 
using string_assign_to = erase_assignment_to<std::string>; 

如上所述,它類似於擦除到==的類型。我做了一些適度的改進(void返回類型)。完美轉發(以T{})構造函數會比block_deduction<U>&&一個更好的(當你{},而不是{{}}建設)。

+0

@dyp我的臉是紅的。刪除不正確的子句。 – Yakk

+0

嗯,在Howard Hinnant提到它之前,我也完全忘記了這個特性;) - 儘管我不完全確定它是否適合這個特性。 'is_convertible'用'std :: string const&'參數模擬函數的限制。正如T.C.注意,你可以給一個'string'分配一個'double',但是你不能將'double'轉換爲'string'。 – dyp

+0

@dyp我認爲'string'中的缺陷比其他任何東西都重要。無論如何,添加了這種類型的刪除實現。您現在可以使用'erase_assignment_to '或'string_assign_to'作爲靜態參數。如果你的代碼只是一個單一的東西,那很愚蠢,但是對於一個新用戶來說,使用它比手動完美轉發更容易。 – Yakk

4

有一個在幻燈片上的錯誤,它應該是std::enable_if<std::is_same<...,而事實上,有在通話過程中實際顯示的幻燈片沒有錯誤,你可以看到它at 1:16:58

enter image description here

是的,正如@dyp指出的那樣,std::enable_if_t<std::is_convertible<String, std::string>::value>>更有意義。

+1

如果函數只有在函數參數是'std :: string'的情況下才能使用,那麼'char const *'參數不會帶來好處。 (實際上,如果沒有額外的重載或手動轉換,你將無法傳遞'char const *')。 – dyp

+2

@dyp:該技術可以調整爲使用'std :: is_assignable '而不是。作者的一般準則是查看你的實現正在做什麼,並使用最能描述你的實現的特性來滿足你的模板約束。 –

+0

這就是我想的,但作爲dyp提及使用'std :: is_convertible '而不是更好地幫助回答我的問題 – bmanga