2017-09-25 90 views
5

我想std :: enable_if第一次和掙扎。 任何指導將不勝感激。enable_if與複製構造函數

當作玩具例子,這裏是一個簡單的靜態矢量類,爲此我要定義一個拷貝構造函數,但行爲取決於載體的相對大小:

  1. 只是將數據複製到更小或相同大小的矢量
  2. 複製數據到一個較大的載體中,然後墊用零

其餘部分,以便所述載體類是:

template <size_t _Size> 
class Vector 
{ 
    double _data[_Size]; 

public: 
    Vector() 
    { 
     std::fill(_data, _data + _Size, 0.0); 
    } 

    const double* data() const 
    { 
     return _data; 
    } 
... 
}; 

拷貝構造函數應該支持這樣的事情,第3版第2個元素複製到V2:

Vector<3> v3; 
Vector<2> v2(v3); 

我試過行爲1.像這樣的拷貝構造函數,編譯:

template <size_t _OtherSize, 
    typename = typename std::enable_if_t<_Size <= _OtherSize>> 
    Vector(const Vector<_OtherSize>& v) : Vector() 
    { 
     std::copy(v.data(), v.data() + _Size, _data); 
    } 

,但編譯器無法將其與行爲2區分開來,即使enable_if條件是互斥的。

template <size_t _OtherSize, 
    typename = typename std::enable_if_t<_OtherSize < _Size>> 
    Vector(const Vector<_OtherSize>& v) : Vector() 
    { 
     std::copy(v.data(), v.data() + _OtherSize, _data); 
     std::fill(_data + _OtherSize, _data + _Size, 0.0); 
    } 

我也試圖把enable_if的說法相反,但它不能推斷_OtherSize的價值:

template <size_t _OtherSize> 
    Vector(const typename std::enable_if_t<_Size <= _OtherSize, 
    Vector<_OtherSize>> & v) 
    : Vector() 
    { 
     std::copy(v.data(), v.data() + _Size, _data); 
    } 

什麼是做到這一點(使用enable_if,不是一個簡單的最佳方式如果聲明)?

感謝

+4

根據定義,複製構造函數不能是模板。你可以有一個模板化的構造函數來複制,但它仍然不會是一個複製構造函數。 ; - ] – ildjarn

+1

這不是問題,但是以下劃線開頭的名稱後跟大寫字母('_Size','_OtherSize')和包含兩個連續下劃線的名稱將保留供實施使用。不要在你的代碼中使用它們。 –

+1

不相關,但在'std :: enable_if_t'之前不需要'typename'。 – Oktalist

回答

7

忽略默認的這些構造的簽名是

template <size_t N, typename> 
Vector(const Vector<N>&) 

也就是說,它們最終是相同的。

一來區分它們的方法是使模板參數類型直接依賴於enable_if的條件:

template <size_t _OtherSize, 
    std::enable_if_t<(_Size <= _OtherSize), int> = 0> 
    Vector(const Vector<_OtherSize>& v) : Vector() 
    { 
     std::copy(v.data(), v.data() + _Size, _data); 
    } 

template <size_t _OtherSize, 
    std::enable_if_t<(_OtherSize < _Size), int> = 0> 
    Vector(const Vector<_OtherSize>& v) : Vector() 
    { 
     std::copy(v.data(), v.data() + _OtherSize, _data); 
     std::fill(_data + _OtherSize, _data + _Size, 0.0); 
    } 

順便說一句,像_Size_OtherSize名是留給實施,因此用戶代碼非法 - 丟失下劃線和/或大寫字母。

此外,正如@StoryTeller暗示的那樣,當編譯器生成的拷貝構造函數具有理想行爲時,您不希望第一個構造函數適用於_OtherSize == _Size。所述構造函數對於相同大小的Vector s已經沒有專門的複製構造函數了,所以在重載過程中不會被選中,但最好通過將<=切換爲<來使其清晰。

+1

對你的評論,'<='是有問題的,應該真的是'<'。如果只是爲了防止在調用「錯誤」c'tor時出現混淆。 – StoryTeller

+2

@StoryTeller:你的意思是這樣的構造函數不適用,而不是編譯器生成的copy-c'tor?是的,可能,但我試圖限制對代碼的語言問題的回答,而不是邏輯問題(OP未提及)。雖然好點,但我會記下它 - 謝謝! – ildjarn

+2

永遠不會在重載解析期間應用。沒有模棱兩可的風險,除了OP想知道爲什麼他們的斷點未被命中:) – StoryTeller

4

請勿使用_Cap;它們被保留用於執行。事實上,標準來源使用了tgese名稱,因爲它們是保留的。不要模仿std/system頭的內部命名約定。

template <size_t O> 
Vector(const Vector<O>& v) : Vector() 
{ 
    constexpr auto to_copy = (std::min)(O, Size); 
    constexpr auto to_fill = Size-to_copy; 
    auto const* src=v.data(); 
    std::copy(src, src + to_copy, _data); 
    std::fill(_data + to_copy, _data + to_copy+to_fill, 0.0); 
} 
Vector(const Vector& v) = default; 

你會發現這優化到你想要的代碼;在沒有填充的情況下,std::fill(foo, foo, 0.0)一起被調用,並且thr體是一個循環,當通過同一個指針兩次時,易於證明是編譯器的noop。

+0

您是否在編寫'(std :: min)',以便將它解析爲表達式並且將std :: min'視爲非限定ID?如果是,爲什麼? – YSC

+3

@YSC:避免windows.h宏shenanigans是一個猜測。 – ildjarn