从初始化列表(对于具有构造函数参数的类)的就地矢量构造 [英] in-place vector construction from initialization list (for class with constructor arguments)
问题描述
可能重复:
我可以列表初始化一个只移动类型的向量吗?
编辑1:请考虑重新开放的投票:我的问题强调就地建设。移动施工是一个选择,但不是这个问题是什么。感谢您的答案!
编辑2:由于我不能回答这个问题(它已关闭)我在这里发表自己的建议。以下是不如我接受的答案好,但可能对其他人有用。至少只有move构造函数被调用:
std :: vector< A2> vec;
{
std :: array< A2,3>数字{{2,3},{5,6},{7,8}}};
vec.reserve(numbers.size());
for(auto& v:numbers)vec.emplace_back(std :: move(v));
}
原文:
$ b
在考虑这个问题的答案时:在STL向量数组中初始化类我发现我找不到一种从初始化列表中获取该向量的就地构造的方法。
<$> 现在试图更清楚,我想要这个(完全正确)初始化 p $ p>
std :: vector< A2> k {{2,3},{4,5},{8,9}};
具有更类似的效果:
std :: vector< A2> k2;
k2.reserve(3);
k2.emplace_back(2,3);
k2.emplace_back(4,5);
k2.emplace_back(8,9);
但是,在第一种情况下, 临时插入。有没有办法避免呢?
std: :载体< A2> k {{2,3},{4,5},std :: move(A2 {8,9})};
但是会产生对移动构造函数的附加调用,没想到。
完整示例:
pre> #include< vector>
#include< iostream>
struct A2 {
int mk;
int mj;
A2(int k,int j):mk(k),mj(j){
std :: cout< constr for<<< this< :<< mk<< std :: endl;
}
A2(const A2& a2){
mk = a2.mk;
mj = a2.mj;
std :: cout<< copy constr for<<< this< :<< mk<< std :: endl;
}
A2(A2& a2)noexcept {
mk = std :: move(a2.mk);
mj = std :: move(a2.mj);
std :: cout<< move constr for<<< this< :<< mk<< std :: endl;
}
};
struct Ano {
Ano(){
std :: cout< constr for<< this<< std :: endl;
}
Ano(const Ano& ano){
std :: cout< copy constr for<< this<< std :: endl;
}
Ano(Ano& ano)noexcept {
std :: cout< move constr for<< this<< std :: endl;
}
};
int main(){
//这里调用构造函数和拷贝构造函数:
std :: vector< A2> k {{2,3},{4,5},std :: move(A2 {8,9})};
std :: cout<< ......<<< std :: endl;
std :: vector< A2> k2;
k2.reserve(3);
//这里(当然)只有构造函数被调用:
k2.emplace_back(2,3);
k2.emplace_back(4,5);
k2.emplace_back(8,9);
std :: cout<< ......<<< std :: endl;
//这里只调用构造函数:
std :: vector< Ano> anos(3);
}
输出:
constr for 0xbf9fdf18:2
constr for 0xbf9fdf20:4
constr for 0xbf9fdf0c:8
move constr for 0xbf9fdf28:8
copy constr for 0x90ed008:2
copy constr for 0x90ed010:4
copy constr for 0x90ed018:8
......
constr for 0x90ed028:2
约束0x90ed030:4
constr for 0x90ed038:8
......
constr为0x90ed048
constr为0x90ed049
constr为0x90ed04a
列表初始化 std :: vector
与你的代码片段没有什么不同,如果 initializer_list
有一个公开的非显式构造函数或 std :: vector
接受数组引用。):
//直接使用'initializer_list' $ b std :: vector< A2> v(别名< A2 [2],A2(2,3),A2(4,5),A2(8,9)
这不是构建 std :: vector
可以利用的实现,真的。列表初始化是统一初始化类型的通用方式。因此,没有任何方法可以 std :: vector 任何不同于任何其他用户定义的类型。因此,使用OP中的构造是可行的。
现在,支持数组(或任何常数数组)内存的实现,这就是为什么
std :: initializer_list< T> :: iterator
只是
typedef T const * iterator;
因此移出 std :: initializer_list
也是没问题的。
现在,有没有解决方案?是的,有,这是一个相当容易的,实际上!
我们想要一个自由的函数,需要一个容器和一些元组等于数的元素你想要的地方。元组容器将容器类型的构造函数的参数。理论上很简单,在实践中很容易使用指数技巧(其中<$ c $代码中的c> indices == seq 和 build_indices == gen_seq
#include< type_traits>
#include< tuple>
#include< utility>
template< class T>使用别名= T;
template< class T>使用RemoveRef = typename std :: remove_reference< T> :: type;
template< class C,unsigned ... Is,class Tuple>
void emplace_back_one(C& c,seq< Is ...>,Tuple&& ts){
c.emplace_back(std :: get< Is>(std :: forward< Tuple& (ts))...);
}
template< class T> using Size = std :: tuple_size< RemoveRef< T>> ;;
template< class C,class ... Tuples>
void emplace_back(C& c,Tuples&&ts ...){
c.reserve(sizeof ...(Tuples));
alias< char []> {(
emplace_back_one(c,gen_seq< std :: tuple_size< RemoveRef< Tuples>> :: value> {},std :: forward< Tuples> ))
,'0')...};
}
上面的代码调用 emplace_back_one
完全 sizeof ...(Tuples)
times,每次传递一个元组的顺序是传递给 emplace_back
。此代码也按从左到右的顺序排列,这意味着构造函数以与为其传递元组相同的顺序调用。 emplace_back_one
然后简单地解开包含索引技巧的元组,并将参数传递给 c.emplace_back
。
Possible Duplicate:
Can I list-initialize a vector of move-only type?
Edit 1: Please consider a re-open vote: My question emphasize in-place construction. Move construction is an alternative but not what this questions is about. Thanks for the answers!
Edit 2: Since I can't answer this question (It got closed) I post my own suggestion here. The following is not as good as the answers I accepted, but may be useful for others. At least only the move constructor is called:
std::vector<A2> vec;
{
std::array<A2,3> numbers{{{2,3},{5,6},{7,8}}};
vec.reserve(numbers.size());
for (auto &v: numbers) vec.emplace_back(std::move(v)) ;
}
Original post:
When thinking about the answer to this question: Initialization of classes within an STL array of vectors I found that I could not find a way to get in-place construction of the vector from an initialization list. What am I missing?
Now trying to be more clear, I would like this (perfectly correct) initialization
std::vector<A2> k{{2,3},{4,5},{8,9}};
to have an effect more similar to this:
std::vector<A2> k2;
k2.reserve(3);
k2.emplace_back(2,3);
k2.emplace_back(4,5);
k2.emplace_back(8,9);
However, in the first case the copy constructor is called for A2 on a temporary while inserting. Is there a way to avoid that? What does the standard say?
I desperately tried
std::vector<A2> k{{2,3},{4,5},std::move(A2{8,9})};
but that generates an additional call to the move constructor, something I also did not expect. I just wanted to explicitly hint that A2 is a temporary, something I had thought was implied.
Full example:
#include <vector>
#include <iostream>
struct A2 {
int mk;
int mj;
A2(int k,int j) : mk(k),mj(j) {
std::cout << " constr for "<<this<< ":"<< mk<<std::endl;
}
A2(const A2& a2) {
mk=a2.mk;
mj=a2.mj;
std::cout << "copy constr for "<<this<< ":" << mk<<std::endl;
}
A2(A2&& a2) noexcept {
mk=std::move(a2.mk);
mj=std::move(a2.mj);
std::cout << "move constr for "<<this<< ":"<< mk<<std::endl;
}
};
struct Ano {
Ano() {
std::cout << " constr for "<<this <<std::endl;
}
Ano(const Ano& ano) {
std::cout << "copy constr for "<<this<<std::endl;
}
Ano(Ano&& ano) noexcept {
std::cout << "move constr for "<<this<<std::endl;
}
};
int main (){
// here both constructor and copy constructor is called:
std::vector<A2> k{{2,3},{4,5},std::move(A2{8,9})};
std::cout << "......"<<std::endl;
std::vector<A2> k2;
k2.reserve(3);
// here (naturally) only constructor is called:
k2.emplace_back(2,3);
k2.emplace_back(4,5);
k2.emplace_back(8,9);
std::cout << "......"<<std::endl;
// here only constructor is called:
std::vector<Ano> anos(3);
}
Output:
constr for 0xbf9fdf18:2
constr for 0xbf9fdf20:4
constr for 0xbf9fdf0c:8
move constr for 0xbf9fdf28:8
copy constr for 0x90ed008:2
copy constr for 0x90ed010:4
copy constr for 0x90ed018:8
......
constr for 0x90ed028:2
constr for 0x90ed030:4
constr for 0x90ed038:8
......
constr for 0x90ed048
constr for 0x90ed049
constr for 0x90ed04a
List-initializing std::vector
in your snippet is no different from doing the following (if initializer_list
had a public non-explicit constructor or std::vector
accepted an array reference.):
// directly construct with the backing array of 'initializer_list'
std::vector<A2> v(alias<A2[]>{ A2(2,3), A2(4,5), A2(8,9) });
It's not a special way to construct a std::vector
that could take advantage of the implementation, really. List-initialization is a general-purpose way of "uniformly" initializing types. As such, there's no way it could tread std::vector
any different than any other user-defined type. As such, having the construct in the OP do emplace construction is out of question.
Now, the backing array (or any constant array) may be put in read-only memory by the implementation, that's the reason why
std::initializer_list<T>::iterator
is just
typedef T const* iterator;
So moving out of std::initializer_list
is also out of question.
Now, is there a solution? Yes, there is, and it's a rather easy one, actually!
We will want to have a free function that takes a container and a number of tuples equal to the number of elements you want to emplace. The tuples container the arguments to the constructor of the container type. Easy in theory, easy in practice with the indices trick (where indices == seq
and build_indices == gen_seq
in the code):
#include <type_traits>
#include <tuple>
#include <utility>
template<class T> using alias = T;
template<class T> using RemoveRef = typename std::remove_reference<T>::type;
template<class C, unsigned... Is, class Tuple>
void emplace_back_one(C& c, seq<Is...>, Tuple&& ts){
c.emplace_back(std::get<Is>(std::forward<Tuple>(ts))...);
}
template<class T> using Size = std::tuple_size<RemoveRef<T>>;
template<class C, class... Tuples>
void emplace_back(C& c, Tuples&&... ts){
c.reserve(sizeof...(Tuples));
alias<char[]>{(
emplace_back_one(c, gen_seq<std::tuple_size<RemoveRef<Tuples>>::value>{}, std::forward<Tuples>(ts))
, '0')...};
}
Live example with the implementation of seq
and gen_seq
.
The code above calls emplace_back_one
exactly sizeof...(Tuples)
times, passing one tuple at a time in the order that the were passed to emplace_back
. This code is also sequenced left-to-right, meaning that the constructors get called in the same order that you passed the tuples for them. emplace_back_one
then simply unpacks the tuple with the indices trick and passes the arguments to c.emplace_back
.
这篇关于从初始化列表(对于具有构造函数参数的类)的就地矢量构造的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!