方便的
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;
}
如果我理解正确的话,这是因为取消引用初始化迭代器会给出
const T
,即使在尝试移动时也会被复制。
有解决方法吗?
阅读https://stackoverflow.com/a/44307741/889742,提出了一种使用可变参数模板的解决方案,如下:
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)...);
}
(
aside1:这声明了一个数组,它会被优化掉吗?
aside2:
void
在表达中的含义和目的是什么?好像没必要
)
随着 C++20 和 C++23 的新变化,现在是否有更好的解决方法?
如果您可以等待 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
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
编译时类型
<A>
参数不是强制性的,可以推导出来,前提是所有参数都具有相同的类型。
如果 exra cullly 括号很烦人,std::make_array
正在测试中。目前还没有按标准出货,但是会省略std::to_array
需要的花括号。
另一种选择是接受原始数组作为函数输入并使用 std::move_itarator
:
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()});
这个看起来像
std::to_array
版本,但它不是 std 库的一部分——因为数组是特殊情况。
有很多可能的组合,但几乎所有组合都依赖于复制省略——它从 C++11 开始作为优化选项,并从 C++17 开始一直是强制性的。