假设您有一个类型变量,std::vector<std::string>并使用初始化列表初始化它:
using V = std::vector<std::string>;
V v = { "Hello", "little", "world", "of", "move", "semantics" };
Run Code Online (Sandbox Code Playgroud)
编译器将为std::string每个字符串文字创建一个临时文件,在这些文件上创建一个初始化列表,然后调用ctor V并创建向量.ctor不知道所有这些字符串都是临时字符串,所以它正在复制每个字符串.
我没有在标准中找到任何允许矢量ctor在临时时移动元素的东西.
我错过了什么或使用初始化程序列表导致不必要的副本?我正在编写类,这个问题可能会导致代码效率低下.任何避免不必要的副本的技术将不胜感激.
这个问题建立在这个@ FredOverflow的问题上.
澄清:
initializer_list需要方法,因为VC++ 2012有一个错误,阻止了命名空间参数的转发扩展._MSC_VER <= 1700有错误.
我编写了一个可变参数模板函数,它可以折叠类型化容器中的任意数量的参数.我使用类型的构造函数将可变参数转换为可使用的值.例如_variant_t:)
MySql在一次打击MySqlVariant中将参数推送到预准备语句时,我需要这个用于我的C++库,而我将输入数据转换为MYSQL_BINDs.因为我可以使用BLOBs,所以当我可以move&&使用大容器时,我希望尽可能避免复制构造.
我做了一个简单的测试,发现了initialize_list确实copy-construct为存储的元素,当它超出范围破坏它们.完美...然后我试图将数据移出,initializer_list并且令我惊讶的是,它并lvalues没有rvalues像我预期的那样使用std::move.
有趣的是,就在Going Native 2013之后发生了明显的警告我,移动不动,前进不会前进 ...... 就像水,我的朋友 - 留在思考的深层.
但这并没有阻止我:)我决定const_cast了initializer_list价值观,仍然将它们移出去.需要强制执行驱逐令.这是我的实施:
template <typename Output_t, typename ...Input_t>
inline Output_t& Compact(Output_t& aOutput, Input_t&& ...aInput){
// should I do this? makes sense...
if(!sizeof...(aInput)){
return aOutput; …Run Code Online (Sandbox Code Playgroud) 最近,我有以下情况
struct data {
std::vector<int> V;
};
data get_vector(int n)
{
std::vector<int> V(n,0);
return {V};
}
Run Code Online (Sandbox Code Playgroud)
这段代码的问题在于,在创建结构体时会发生一个副本,而解决方案是编写return {std::move(V)}
是否有可以检测这种虚假复制操作的 linter 或代码分析器?cppcheck、cpplint 和 clang-tidy 都做不到。
编辑:几点让我的问题更清楚:
我有以下非常简单的课程:
class Foo
{
public:
Foo() {}
Foo(const Foo&) = delete;
Foo(Foo&&) {}
void operator=(const Foo&) = delete;
void operator=(Foo&&) {}
void dump() const {}
};
Run Code Online (Sandbox Code Playgroud)
该类是可构造和可分配的,但不是可复制的可分配的.
我想使用vector的初始化列表初始化Foo元素的向量.
std::vector<Foo> vf = { Foo() };
Run Code Online (Sandbox Code Playgroud)
编译器抱怨,因为代码必须使用已删除的复制构造函数.任何人都可以解释一下,为什么在这种情况下不使用移动构造,以及为什么需要复制对象?
以下还需要复制构造函数,但不起作用:
std::vector<Foo> vf = { std::move(Foo()) };
Run Code Online (Sandbox Code Playgroud)
另一方面,这工作正常(调用移动构造函数):
std::vector<Foo> vf;
vf.push_back(Foo());
Run Code Online (Sandbox Code Playgroud)
感谢您的解释... :)
更新:
建议这篇文章解释我的问题.
此外,让我们考虑以下代码(以及class Foo上面的代码):
class Bar {
public:
Bar(std::initializer_list<Foo> _l) {
std::cout << "Bar::Bar()" << std::endl;
for (auto& e : _l)
e.dump();
}
};
int main() {
Bar …Run Code Online (Sandbox Code Playgroud) 可能重复:
我可以列出 - 初始化只移动类型的向量吗?
编辑1:请考虑重新公开投票:我的问题强调就地建设.移动施工是另一种选择,但不是这个问题的内容.谢谢你的回答!
编辑2:因为我无法回答这个问题(它已经关闭)我在这里发表我自己的建议.以下不如我接受的答案好,但可能对其他人有用.至少只调用移动构造函数:
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)) ;
}
Run Code Online (Sandbox Code Playgroud)
原帖:
在考虑这个问题的答案时:在STL矢量数组中初始化类我发现我找不到从初始化列表中获取矢量的就地构造的方法.我错过了什么?
现在想要更清楚,我希望这(完全正确)初始化
std::vector<A2> k{{2,3},{4,5},{8,9}};
Run Code Online (Sandbox Code Playgroud)
有一个更类似的效果:
std::vector<A2> k2;
k2.reserve(3);
k2.emplace_back(2,3);
k2.emplace_back(4,5);
k2.emplace_back(8,9);
Run Code Online (Sandbox Code Playgroud)
但是,在第一种情况下,在插入时临时为复制构造函数调用A2.有没有办法避免这种情况?标准说什么?
我拼命地试了一下
std::vector<A2> k{{2,3},{4,5},std::move(A2{8,9})};
Run Code Online (Sandbox Code Playgroud)
但是这会产生对移动构造函数的额外调用,这也是我没想到的.我只是想明确暗示A2是暂时的,我原以为是隐含的.
完整示例:
#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 …Run Code Online (Sandbox Code Playgroud) 我看到一些错误路过std::vector< std::unique_ptr< T > >周围std::move.重现问题的代码是这样的:
#include <memory> // for std::unique_ptr
#include <utility> // for std::move
#include <vector> // for std::vector
struct bar {};
using vtype = std::vector<std::unique_ptr<bar>>;
struct foo
{
foo(vtype v) : _v(std::move(v)) { }
private:
vtype _v;
};
vtype getVector()
{
return { std::move( std::unique_ptr<bar>(new bar()) ) };
};
int main()
{
foo f(std::move(getVector()));
};
Run Code Online (Sandbox Code Playgroud)
使用clang 3.4,此代码会产生此错误:
$ clang++ -std=c++11 test.cpp -o xtest
In file included from test.cpp:1:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/memory:64:
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/stl_construct.h:75:38: …Run Code Online (Sandbox Code Playgroud) 方便的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 …Run Code Online (Sandbox Code Playgroud) 我有以下代码,不会编译,它是星期五,我有点疲惫.
#include <string>
#include <memory>
#include <utility>
#include <map>
template< typename T, typename ...Args >
std::unique_ptr< T > make_unique( Args && ...args )
{
return std::unique_ptr< T >( new T( std::forward< Args >( args )... ) );
}
struct A
{
};
std::map< std::string, std::unique_ptr< A > > _map = { { "A", make_unique< A >() } }; // <-- ERROR!!
Run Code Online (Sandbox Code Playgroud)
以下编译没有问题
int main()
{
std::pair< std::string, std::unique_ptr< A > > p { "B", make_unique< A >() };
_map.insert( std::make_pair( …Run Code Online (Sandbox Code Playgroud) 考虑以下代码:
#include <iostream>
#include <vector>
struct C {
C() {}
C(const C&) { std::cout << "A copy was made.\n"; }
C(C&&) {std::cout << "A move was made.\n";}
};
std::vector<C> g() {
std::vector<C> ret {C(), C(), C()};
return ret;
}
std::vector<C> h() {
std::vector<C> ret;
ret.reserve(3);
ret.push_back(C());
ret.push_back(C());
ret.push_back(C());
return ret;
}
int main() {
std::cout << "Test g\n";
std::vector<C> v1 = g();
std::cout << "Test h\n";
std::vector<C> v2 = h();
}
Run Code Online (Sandbox Code Playgroud)
编译g++ -std=c++11 main.cpp && ./a.out,结果是:
Test g …Run Code Online (Sandbox Code Playgroud) 我可以简洁地(用大括号)初始化以下 6 种情况中的 5 种:
\n| 可复制的 | 只移动的 | |
|---|---|---|
| 大批 | 是的 | 是的 |
| 标准::数组 | 是的 | 是的 |
| std::向量 | 是的 | 不 |
一种似乎不起作用的情况是尝试初始化仅移动对象的 std::vector ;无法编译,并显示消息,例如:“错误:调用 \'std::unique_ptr\' 的隐式删除复制构造函数”。
\n为什么是这样?是否有适用于这种情况的替代初始化语法?
\n下面的程序演示了。
\n/*\nclang++ -std=c++14 -W -Wall -Werror question.cc -o question\nclang++ -std=c++17 -W -Wall -Werror question.cc -o question\nclang++ -std=c++20 -W -Wall -Werror question.cc -o question\nclang++ -std=c++2b -W -Wall -Werror question.cc -o question\ng++ -std=c++14 -W -Wall -Werror question.cc -o question\ng++ -std=c++17 -W -Wall -Werror question.cc -o question\ng++ -std=c++20 -W -Wall -Werror question.cc -o …Run Code Online (Sandbox Code Playgroud) c++ ×10
c++11 ×5
stdvector ×2
unique-ptr ×2
c++20 ×1
c++23 ×1
cppcheck ×1
dictionary ×1
move ×1