无法从initializer_list移动​​的解决方法?

Gon*_*n I 9 c++ initializer-list move-semantics c++20 c++23

方便的initializer_list语法似乎是以无法移动列表成员、创建不必要的副本为代价的。

struct  A
{
    // some members which are dynamic resources...
    A() { cout << "Default Constructor\n"; }
    A(const A& original) { cout << "Copy constructor\n"; }
    A(A&& original) { cout << "Move constructor\n";  }
};
    
int main() {
    vector<A> v1{ A() , A() }; // calls copy
    vector<A> v2;
    v2.push_back(A()); v2.push_back(A()); // calls move
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

如果我理解正确,这是因为取消引用初始化器迭代器会给出const T,即使尝试移动时也会复制它。

有解决方法吗?

阅读/sf/answers/3101541901/,提出了一种使用可变参数模板的解决方案,如下所示:

template<class Array> struct maker;

// a maker which makes a std::vector
template<class T, class A>
struct maker<std::vector<T, A>>
{
  using result_type = std::vector<T, A>;

  template<class...Ts>
  auto operator()(Ts&&...ts) const -> result_type
  {
    result_type result;
    result.reserve(sizeof...(Ts));
    using expand = int[];
    void(expand {
      0,
      (result.push_back(std::forward<Ts>(ts)),0)...
    });

    return result;
  }
};

// a maker which makes std::array
template<class T, std::size_t N>
struct maker<std::array<T, N>>
{
  using result_type = std::array<T, N>;

  template<class...Ts>
  auto operator()(Ts&&...ts) const
  {
    return result_type { std::forward<Ts>(ts)... };
  }

};

//
// delegation function which selects the correct maker
//
template<class Array, class...Ts>
auto make(Ts&&...ts)
{
  auto m = maker<Array>();
  return m(std::forward<Ts>(ts)...);
}
Run Code Online (Sandbox Code Playgroud)

(aside1:这声明了一个数组,它会被优化掉吗? aside2:表达式中的
含义和目的是什么?似乎没有必要)void

随着 C++20 和 C++23 的新变化,现在是否有更好的解决方法来解决这一切?

Red*_*ave 6

如果您可以等待 C++23,范围可以提供帮助:

#include <array>
#include <ranges>
auto v= std::array{A(),A()}         //c++17
      | std::views::as_rvalue       //c++23
      | std::ranges::to<std::vector>(); //c++23
Run Code Online (Sandbox Code Playgroud)

array构造函数使用 CTAD 从第一个输入推断出类型,并从输入数量推断出大小。休息是不言自明的。to_array如果仅必须显式指定类型,则可以使用构造函数代替:

#include <array>
#include <ranges>
auto v= std::to_array<A>({A(),A()}) //c++20
      | std::views::as_rvalue       //c++23
      | std::ranges::to<std::vector>(); //c++23
Run Code Online (Sandbox Code Playgroud)

编译时类型<A>参数不是强制性的,只要所有参数都具有相同的类型,就可以推断出编译时类型参数。如果额外的花括号很烦人,std::make_array正在测试中。它尚未按照标准发布,但它将省略std::to_array所需的花括号。另一种选择是接受原始数组作为函数输入并使用std::move_iterator

template<typename T, std::size_t N>
std::vector<T> to_vector(T (&&arr)[N]){
     return std::vector<T>(
            std::make_move_iterator(std::begin(arr)),//c++11
            std::make_move_iterator(std::end(arr)));
};

auto v = to_vector({A(),A()});
Run Code Online (Sandbox Code Playgroud)

这个看起来像这个std::to_array版本,但它不是 std 库的一部分 - 因为数组是特殊情况。有很多可能的组合,但几乎所有组合都依赖于复制省略 - 它最初是 C++11 的优化选项,自 C++17 以来一直是强制的。