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

问题描述

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

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

考虑以下代码:

struct TwoInts {
  TwoInts(int x, int y) 
    : a{x}
    , b{y}
  {}
  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> b{{{1,2}}};
std::array<TwoInts,2> c{{{1,2},{3,4}}};

Holder<int,1> d{1};
Holder<int,2> e{1,2};

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

Holder<TwoInts,1> f{{{1,2}}};
Holder<TwoInts,2> f{{{1,2},{3,4}}};

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

标签: c++c++17

解决方案


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

Holder<TwoInts,1> f{TwoInts{1, 2}};
Holder<TwoInts,2> e{TwoInts{1,2}, TwoInts{3,4}};

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

请注意,在std::array<TwoInts,1> b{{{1,2}}};您不使用std::initializer_list,而是执行聚合初始化,并且在 中Holder<int,1> d{1};,您只需传递一个int,这可以很好地进行演绎。


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

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

   int a,b;
};

然后在整个过程中使用聚合初始化。持有者构造函数应该如下所示;

template<typename... Args>
   Holder(Args&&... args) : m{{std::forward<Args>(args)...}} {}
   //                        ^ no parentheses  ... neither ^

你可以初始化

Holder<TwoInts,1> f{1, 2};
Holder<TwoInts,2> e{1,2, 3,4};

推荐阅读