带有默认值的C ++可变参数模板函数参数

时间:2013-02-11 02:45:39

标签: c++ c++11 default-value variadic-templates variadic-functions

我有一个函数,它带有一个带默认值的参数。现在我还希望它采用可变数量的参数并将它们转发给其他一些函数。默认值的函数参数必须是最后一个,所以...我可以将该参数放在可变参数包之后,编译器会在调用函数时检测我是否提供它吗?

(假设包不包含那个最后一个参数的类型。如果有必要,我们可以假设,因为该类型通常不应该被用户知道,否则它被认为是我的界面的错误用法无论如何....)

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

2 个答案:

答案 0 :(得分:17)

不,包必须是最后一个。

但你可以假装它。您可以检测包中的最后一个类型。如果是SomeSpecialType,您可以运行您的功能。如果不是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))) );
}

或类似的东西。

答案 1 :(得分:5)

另一种方法是通过元组传递可变参数。

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