2013-02-11 129 views
17

我有一個函數,它帶有一個默認值的參數。現在我也希望它獲取可變數量的參數並將它們轉發給其他函數。具有默認值的函數參數必須是最後一個,所以...我可以把這個參數放在可變參數包之後,編譯器會在調用函數時檢測我是否提供它? (假設該包不包含那個最後一個參數的類型,如果需要的話,我們可以假設,因爲該類型通常不應該被用戶知道,否則被認爲是我的錯誤用法)。接口無論如何....)具有默認值的C++ variadic模板函數參數

template <class... Args> 
void func (Args&&... args, SomeSpecialType num = fromNum(5)) 
{ 
} 

回答

16

不,包必須是最後一個。

但你可以僞造它。您可以檢測包裝中的最後一種類型。如果它是SomeSpecialType,你可以運行你的func。如果它不是SomeSpecialType,則可以遞歸地調用你自己的轉發參數並附加fromNum(5)

如果您想要使用SFINAE技術,可以在編譯時(即不同的超載)完成此項檢查。但是這可能是不值得的麻煩,考慮到「運行時間」檢查將在給定的過載情況下保持不變,因此幾乎肯定會被優化,而SFINAE不應該被輕易使用。

這不會給你你想要的簽名,但它會給你你想要的行爲。您必須在評論中解釋預期的簽名。

這樣的事情,以後你刪除錯別字和其他:

// extract the last type in a pack. The last type in a pack with no elements is 
// not a type: 
template<typename... Ts> 
struct last_type {}; 
template<typename T0> 
struct last_type<T0> { 
    typedef T0 type; 
}; 
template<typename T0, typename T1, typename... Ts> 
struct last_type<T0, T1, Ts...>:last_type<T1, Ts...> {}; 

// using aliases, because typename spam sucks: 
template<typename Ts...> 
using LastType = typename last_type<Ts...>::type; 
template<bool b, typename T=void> 
using EnableIf = typename std::enable_if<b, T>::type; 
template<typename T> 
using Decay = typename std::decay<T>::type; 

// the case where the last argument is SomeSpecialType: 
template< 
    typename... Args, 
    typename=EnableIf< 
    std::is_same< 
     Decay<LastType<Args...>>, 
     SomeSpecialType 
    >::value 
    > 
void func(Args&&... args) { 
    // code 
} 

// the case where there is no SomeSpecialType last:  
template< 
    typename... Args, 
    typename=EnableIf< 
    !std::is_same< 
     typename std::decay<LastType<Args...>>::type, 
     SomeSpecialType 
    >::value 
    > 
void func(Args&&... args) { 
    func(std::forward<Args>(args)..., std::move(static_cast<SomeSpecialType>(fromNum(5)))); 
} 

// the 0-arg case, because both of the above require that there be an actual 
// last type: 
void func() { 
    func(std::move(static_cast<SomeSpecialType>(fromNum(5)))); 
} 

或東西很像。

+0

所以這就像一個解決方法,它是一個不同的簽名,但是同樣的行爲......我明白了。實際上,我打算在將來刪除這個參數,所以也許這不值得(但簽名會令人困惑)。你能告訴我一個簡單的例子嗎? – cfa45ca55111016ee9269f0a52e771 2013-02-11 02:53:57

+0

@ fr33domlover我勾勒出了設計。尚未編譯,更不用說調試了,但基本原理應該在那裏。 – Yakk 2013-02-11 03:24:54

+0

謝謝,我會試試看,如果我不只是決定刪除單個參數。它看起來很複雜,並且簽名沒有保存,所以它可能不值得麻煩......無論如何感謝 – cfa45ca55111016ee9269f0a52e771 2013-02-11 03:30:53

3

另一種方法是通過元組傳遞可變參數。

template <class... Args> 
void func (std::tuple<Args...> t, SomeSpecialType num = fromNum(5)) 
{ 
    // don't forget to move t when you use it for the last time 
} 

優點:接口更簡單,重載和添加默認值參數很容易。

缺點:調用者必須在std::make_tuplestd::forward_as_tuple調用中手動包裝參數。此外,您可能不得不求助於std::index_sequence技巧來實現該功能。