2013-02-11 128 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技巧来实现该功能。