首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >具有默认值的C++可变模板函数参数

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

Stack Overflow用户
提问于 2013-02-11 10:45:39
回答 4查看 10.6K关注 0票数 26

我有一个函数,它接受一个带有默认值的参数。现在我还想让它接受可变数量的参数,并将它们转发给其他函数。带有默认值的函数参数必须是最后一个,因此...我可以把这个参数放在变量包之后,编译器会在调用函数时检测到我是否提供了它吗?

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

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

回答 4

Stack Overflow用户

回答已采纳

发布于 2013-02-11 10:48:01

不,包必须是最后一包。

但你可以假装。您可以检测到包中的最后一个类型是什么。如果是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))) );
}

或者类似的东西。

票数 22
EN

Stack Overflow用户

发布于 2015-09-28 15:32:51

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

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技巧来实现该函数。

票数 9
EN

Stack Overflow用户

发布于 2021-12-26 07:33:43

这来得有点晚,但在C++17中,你可以用std::tuple来做这件事,总体来说它会相当不错。这是@xavlour答案的扩展:

template <class... Args>
void func (std::tuple<Args&&...> t, SomeSpecialType num = fromNum(5))
{
    // std::apply from C++17 allows you to iterate over the tuple with ease
    // this just prints them one by one, you want to do other operations i presume
    std::apply([](auto&&... args) {((std::cout << args << '\n'), ...);}, t);
}

然后,创建一个简单的函数来准备它们:

template<typename... Args>
std::tuple<Args&&...> MULTI_ARGS(Args&&... args) {
    return std::tuple<Args&&...>(args...);
}

现在您可以像这样调用该函数:

func(MULTI_ARGS(str1, int1, str2, str3, int3)); // default parameter used
func(MULTI_ARGS(str1, int1, str2));  // default parameter used
func(MULTI_ARGS(str1, int1, str2, str3, int3, otherStuff), fromNum(10)); // custom value instead of default

免责声明:我在设计记录器时遇到了这个问题,希望有一个包含std::source_location::current()的默认参数,就我所能找到的而言,这是确保调用者信息被准确传递的唯一方法。创建函数包装器将更改source_location信息以表示包装器,而不是原始调用者。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14805192

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档