如何通过可变参数模板将多个构造函数参数转发到数组初始值设定项列表?

Posted

技术标签:

【中文标题】如何通过可变参数模板将多个构造函数参数转发到数组初始值设定项列表?【英文标题】:How to forward multiple constructor arguments through a variadic template to an array initializer list? 【发布时间】:2019-10-11 09:53:15 【问题描述】:

我正在尝试通过可变参数模板传递多个初始值设定项列表以在适当位置构造 std::array。

但是我找不到可以编译的版本 (C++17)。

考虑以下代码:

struct TwoInts 
  TwoInts(int x, int y) 
    : ax
    , by
  
  int a,b;
;

template<typename T, int S>
struct Holder 
  template<typename... Args>
  Holder(Args&&... args) : m(std::forward<Args>(args)...) 

  //Deleted for clarity of the example:
  Holder(const Holder& o) = delete;
  Holder(Holder&& o) = delete;
  std::array<T,S> m;
;

我们可以执行以下初始化:

TwoInts a = 1,2;
std::array<TwoInts,1> b1,2;
std::array<TwoInts,2> c1,2,3,4;

Holder<int,1> d1;
Holder<int,2> e1,2;

但是如何修改示例以使其工作?

Holder<TwoInts,1> f1,2;
Holder<TwoInts,2> f1,2,3,4;

Gcc 和 clang 找不到匹配的构造函数。

【问题讨论】:

【参考方案1】:

您可以明确地说明参数类型,而不是隐式传递 std::initializer_lists:

Holder<TwoInts,1> fTwoInts1, 2;
Holder<TwoInts,2> eTwoInts1,2, TwoInts3,4;

这是由于 std::initializer_lists 会产生所谓的“非推导”上下文,因此您不能使用 std::initializer_lists 将它们传递给模板构造函数(因为该语言不允许您显式确定构造函数模板的模板类型)。

请注意,在std::array&lt;TwoInts,1&gt; b1,2; 中您不使用std::initializer_list,而是执行聚合初始化,而在Holder&lt;int,1&gt; d1; 中,您只需传递一个int,它可以很好地用于推导。


如果TwoInt 对象不可移动/复制构造,您可以将它们变成聚合;

struct TwoInts 
   TwoInts(const TwoInts&) = delete;
   TwoInts(TwoInts&&) = delete;

   int a,b;
;

然后在整个过程中使用聚合初始化。持有者构造函数应该是这样的;

template<typename... Args>
   Holder(Args&&... args) : mstd::forward<Args>(args)... 
   //                        ^ no parentheses  ... neither ^

你可以初始化

Holder<TwoInts,1> f1, 2;
Holder<TwoInts,2> e1,2, 3,4;

【讨论】:

没错,这行得通,但它依赖于TwoInts 是可移动或可复制构造的,因此在某种程度上回避了这个问题。如果不是这样呢? @RedundantEntry 查看编辑。不过,我没有想到适用于这两种情况的解决方案。 哇,我不知道这会奏效。当然,这又是对元素类型TwoInts 的假设。但我想没有它就没有办法完成这项工作。

以上是关于如何通过可变参数模板将多个构造函数参数转发到数组初始值设定项列表?的主要内容,如果未能解决你的问题,请参考以下文章

如何将构造函数(可变参数)作为模板参数传递?

可变参数模板模板和完美转发

C++ 为类模板提供初始化列表构造函数

如何在可变参数模板中有多个参数包?

编写可变参数模板构造函数

可变参数模板和通过赋值的复制构造