2012-07-20 19 views
4

讓我們考慮一個簡單的類右值引用和構造函數參數

template< typename T > 
class Wrapper { 
public: 
    // Constructors? 
private: 
    T wrapped; 
}; 

它應該使用什麼樣的構造是有效的?


C++之前0X會有一個構造函數之一:

  1. const引用(T const&) - 如果類型是T 「重」,
  2. 或值(T) - 如果類型T是「輕」。

確定類型T是「重」還是「輕」並不容易。

人們可以假設只有內建類型(int/floats/...)是「輕」。但這並不完全正確,因爲我們自己的Wrapper<int>最有可能也應該被視爲「輕」類型。

boost::call_traits這樣的庫提供了一些手段,通過允許類型創建者將類型標記爲「輕」(通過提供適當的call_traits專業化)來克服此困難。否則它將被視爲「重」。似乎可以接受。


但是,C++ 0x使情況變得更糟。因爲現在你也有參考(T&&),它允許有效地採取(一些)「重」的對象。

因爲這個現在你必須之中選擇和:

  1. 只是const引用(T const&) - 如果類型T是「重」,不支持移動語義(因爲無論有沒有 - 像大莢 - 或沒有寫,你有沒有影響),
  2. 兩個const引用(T const&)和右值引用(T&&) - 如果類型T是「重」,不支持移動語義,
  3. 只值(T) - 如果T類型是「輕量級」或者是「重量級」但支持移動語義(即使進行了複製,也不會打擾使用,否則我們不得不自己複製T const& ...)。

還不容易分辨哪些類型是「重」,哪些類型是「輕」(如以前)。但是現在你也不知道類型T是否支持移動語義(或者你是否?)。


一旦你換一個以上的值,因爲可能的構造函數重載數量呈指數級增長,這變得更加惱人。

有沒有解決這個問題的方法?

我雖然關於轉發(完美轉發)參數的一些模板構造函數,但我不確定是否會按需要工作。而且它還允許提供將被轉發給T構造函數的不同類型的值。這可能被認爲是一個功能,但不一定。

回答

7

相反,C++ 11使它更容易由於普遍引用:

template <typename T> struct Wrapper 
{ 
    T value; 

    template <typename U> Wrapper(U && u) 
    : value(std::forward<U>(u)) 
    { } 
}; 

作爲一個額外的很好的接觸,你要補充一點,只存在違約第二個參數時T是constructible U,以免讓你的類本身看起來可以從不匹配的類型中構造出來。並使其可變參數,也:

template <typename ...Args> 
Wrapper(Args &&... args, 
     typename std::enable_if<std::is_constructible<T, Args...>::value, int>::type = 0) 
: value(std::forward<Args>(args)...) 
{ } 

確保#include <utility>forward#include <type_traits>的特質。

+0

您也可以使用具有相同完美轉發的可變參數構造函數來構造'value'。您只需編寫一個通用構造函數:'template Wrapper(U && ... us):value(std :: forward (us)...){} – 2012-07-20 11:29:43

+0

@AlexandreC:是是的,已經在那:-) – 2012-07-20 11:30:02

+0

只有一個問題:'std :: enable_if'在這裏並不是必須的(它不會改善錯誤報告)。這裏有什麼用處? – 2012-07-20 11:32:36

3

如果您打算複製您的T,最好是按值傳遞參數並讓編譯器找出複製。無論你做什麼,無論如何至少會有一個副本。

template< typename T > 
class Wrapper { 
public: 
  Wrapper(T value) : wrapped(std::move(value)) 
    { } 
private: 
  T wrapped; 
}; 

請參閱Dave Abrahams的Want speed? Pass by value

+0

請注意,如果預計'Wrapper '是'Wrapper'的有效實例化,那麼您想要使用'std :: forward (value)'。 – 2012-07-21 02:25:14

+0

在我看來(從文章中),如果且僅當T是可移動的,這纔會成立。如果不是這樣,這將不是最理想的。在一個你永遠不知道的模板中。 – 2012-07-21 11:58:32

+0

@Adam - 否。如果該類型只有一個拷貝構造函數,則副本可以被省略。您將需要*一個*副本來存儲該值,但編譯器可以優化其他不必要的複製。 – 2012-07-21 12:19:55