【问题标题】:Default initialized (with value initialization) parameter pack默认初始化(带值初始化)参数包
【发布时间】:2015-02-25 12:08:18
【问题描述】:

我可以默认将参数包初始化为每种类型的相应值初始化吗?

再详细一点,以一个简单的函数模板为例

template<typename T>
void f(T arg = T())
{ 
  // eg for T=int, arg is 0 (value initialization) when default initialized
}

是否有可能表达它的可变参数对应物,即

template<typename... Args>
void F(Args... args /* how can I value initialize the parameter pack? */)
{
}

【问题讨论】:

  • F&lt;int, char, float, double&gt;(1, 'c');你想要达到什么目的?
  • @PiotrS。是的,甚至是F&lt;int, char, float, double&gt;()。我想我可以解压缩一个默认的初始化元组,而忘记函数默认参数(即使这不包括你提到的情况)
  • 对不起,我的意思是 value 已初始化。请参阅 Potatoswatter 为任何需要消歧的人提供的答案

标签: c++ c++11 templates variadic-templates


【解决方案1】:
#include <iostream>
#include <utility>
#include <tuple>
#include <cstddef>
#include <type_traits>

template <typename... Args>
void F(Args... args)
{
    // target function, arbitrary body
    using expander = int[];
    (void)expander{ 0, (void(std::cout << args << " "), 0)... };
    std::cout << std::endl;
}

template <typename... Args, typename... Params, std::size_t... Is>
void F(std::index_sequence<Is...>, Params&&... params)
{
    F<Args...>(std::forward<Params>(params)...
             , std::decay_t<typename std::tuple_element<sizeof...(Params) + Is, std::tuple<Args...>>::type>{}...);
}

template <typename... Args, typename... Params>
auto F(Params&&... params)
    -> std::enable_if_t<(sizeof...(Args) > sizeof...(Params))>
{
    F<Args...>(std::make_index_sequence<sizeof...(Args) - sizeof...(Params)>{}
             , std::forward<Params>(params)...);
}

测试:

#include <string>

int main()
{
    // F(int, char, float = float{}, double = double{})
    F<int, char, float, double>(1, 'c');

    // F(int = int{}, char = char{}, float = float{}, double = double{})     
    F<int, char, float, double>();

    // F(const std::string&, const std::string& = std::string{})
    F<const std::string&, const std::string&>("foo");

    // F(int, int, int)
    F(1, 2, 3);
}

输出:

1 'c' 0 0 
0 '\0' 0 0
"foo" ""
1 2 3

DEMO

【讨论】:

  • 好的,你从一开始就获得了我的 +1。但是要接受答案,我必须更多地了解它。我会提到我的理解,纠正我的任何错误:(1)您建议将函数包装在一个调用系统中,该系统将为未提供的提供默认(值初始化)参数:@ 987654327@ 是包装系统,F3 是结束函数 (2) 什么是扩展器?是复合语句吗? (3) Params - Args 将是填写的 args 的数量,Params 可以与 Args 重叠
  • @NorahAttkins F3 是一个目标函数,我只是把expander 放在那里,它只是打印出所有参数,但它的主体可以根据您的需要进行定制。 Params-Args 为您提供未提供参数的显式类型模板参数的数量。最终,那些缺失的参数被初始化并传递给F3
  • 刚刚注意到扩展器中的...。我是不是一个看到这种用途令人惊叹的新手(不要回答这个问题!)。如果你能提供任何关于这个 hack、语言功能、使用......任何东西的链接,那就太好了。谢谢你的回答,我学到了很多!
  • @NorahAttkins 见this explanation
  • 感谢链接。我现在明白了;甚至给了try这个
【解决方案2】:

你可以创建两个参数包,一个代表函数参数对应的类型,一个代表“默认参数”。

template< typename ... aux, typename ... arg >
void fn( arg ... a ) {
    std::tuple< aux ... > more {}; // The tuple elements are value-initialized.
}

http://coliru.stacked-crooked.com/a/1baac4b877dce4eb

没有办法明确提及此函数的推导模板参数。调用的尖括号内的任何内容都将进入aux,而不是arg

注意,您使用{} 获得的初始化是值初始化,而不是默认初始化。基本类型的对象被清零,而不是未初始化。

【讨论】:

  • 哈我只是(同时)在问题的 cmets 中提到了元组破解。很酷的双可变参数模板 args :) +1
  • @NorahAttkins 另一个变体是std::tuple&lt; arg ..., aux ... &gt; more { std::move( a ) ..., aux {} ... };。这将有效地将两个包按您想要的顺序合并回来。
  • @NorahAttkins 等一下,显式给定的类型模板参数去哪里了:它们都是要进行值初始化的辅助参数,还是只有那些没有作为函数调用参数给出的辅助参数? F&lt;int&gt;(1) 有一个参数1,还是两个参数:1 和值初始化的0
  • @PiotrS。我认为原始问题描述会给它一个论点,就像在您的解决方案中一样,但我提出了一个更简单的解决方案,它代表两个论点。您的解决方案似乎是我的扩展以满足该特定要求,您手动跳过初始显式参数。
【解决方案3】:

C++ 标准明确禁止,你不能这样做。 N3376 8.3.6/3

默认参数只能在 函数声明的参数声明子句或在 模板参数(14.1);在后一种情况下,初始化子句 应该是一个赋值表达式。 不应使用默认参数 为参数包指定。

【讨论】:

  • @NorahAttkins 你可以编写两个重载,一个带参数,一个不带参数,第一个使用默认参数,第二个指定。
猜你喜欢
  • 2014-12-29
  • 1970-01-01
  • 2020-02-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多