我更喜欢两种方式:
void copyVecFast(const vec<int>& original)
{
vector<int> newVec;
newVec.reserve(original.size());
copy(original.begin(),original.end(),back_inserter(newVec));
}
void copyVecFast(vec<int>& original)
{
vector<int> newVec;
newVec.swap(original);
}
Run Code Online (Sandbox Code Playgroud)
你怎么做呢?
我知道在C++ 03中,从技术上讲,std::basic_string模板不需要具有连续的内存.但是,我很好奇有多少实现存在于实际利用这种自由的现代编译器.例如,如果想要用来basic_string接收某些C API的结果(如下面的例子),分配一个向量只是为了立即将它变成一个字符串似乎很愚蠢.
例:
DWORD valueLength = 0;
DWORD type;
LONG errorCheck = RegQueryValueExW(
hWin32,
value.c_str(),
NULL,
&type,
NULL,
&valueLength);
if (errorCheck != ERROR_SUCCESS)
WindowsApiException::Throw(errorCheck);
else if (valueLength == 0)
return std::wstring();
std::wstring buffer;
do
{
buffer.resize(valueLength/sizeof(wchar_t));
errorCheck = RegQueryValueExW(
hWin32,
value.c_str(),
NULL,
&type,
&buffer[0],
&valueLength);
} while (errorCheck == ERROR_MORE_DATA);
if (errorCheck != ERROR_SUCCESS)
WindowsApiException::Throw(errorCheck);
return buffer;
Run Code Online (Sandbox Code Playgroud)
我知道像这样的代码可能会略微降低可移植性,因为它意味着它std::wstring是连续的 - 但我想知道这个代码是多么不可移植.换句话说,编译器如何实际利用具有非连续内存的自由?
编辑:我更新了这个问题,提到C++ 03.读者应注意,在定位C++ 11时,标准现在要求basic_string是连续的,因此在定位该标准时,上述问题不是问题.
move对象之后,新旧对象之间是否存在依赖关系?int main () {
int a = 100;
std::cout<<&a<<std::endl;
auto a_copy = a; // deduced as int
std::cout<<&a_copy<<std::endl;
auto a_move = std::move(a); // deduced as int
std::cout<<&a_move<<std::endl;
};
Run Code Online (Sandbox Code Playgroud)
输出:
0x7fffffffe094
0x7fffffffe098
0x7fffffffe09c
Run Code Online (Sandbox Code Playgroud) 我最近意识到在C++ 11中添加移动语义(或者至少我的实现,Visual C++)已经积极地(并且非常显着地)打破了我的一个优化.
请考虑以下代码:
#include <vector>
int main()
{
typedef std::vector<std::vector<int> > LookupTable;
LookupTable values(100); // make a new table
values[0].push_back(1); // populate some entries
// Now clear the table but keep its buffers allocated for later use
values = LookupTable(values.size());
return values[0].capacity();
}
Run Code Online (Sandbox Code Playgroud)
我遵循这种模式来执行容器回收:我将重新使用相同的容器而不是销毁和重新创建它,以避免不必要的堆释放和(立即)重新分配.
在C++ 03上,这很好用 - 这意味着这段代码用于返回1,因为向量是按元素复制的,而它们的底层缓冲区保持原样.因此,我可以修改每个内部向量,因为它知道它可以使用与之前相同的缓冲区.
然而,在C++ 11上,我注意到这会导致右侧移动到左侧,这对左侧的每个向量执行元素移动分配.这反过来导致向量丢弃其旧缓冲区,突然将其容量减少到零.因此,由于堆分配/释放过多,我的应用程序现在会大大减慢速度.
我的问题是:这种行为是一个错误,还是故意的?是否甚至由标准指定?
我刚刚意识到这种特定行为的正确性可能取决于是否a = A()可以使指向元素的迭代器无效a.但是,我不知道移动赋值的迭代器失效规则是什么,所以如果你知道它们,你的答案中可能值得一提.
我很惊讶地得知移动构造函数(以及该事项的赋值)std::optional没有重置可选的移动,如[19.6.3.1/7]中所示,其中"bool(rhs)不变".
这也可以通过以下代码看出:
#include <ios>
#include <iostream>
#include <optional>
#include <utility>
int main() {
std::optional<int> foo{ 0 };
std::optional<int> bar{ std::move(foo) };
std::cout << std::boolalpha
<< foo.has_value() << '\n' // true
<< bar.has_value() << '\n'; // true
}
Run Code Online (Sandbox Code Playgroud)
这似乎与在标准库中移动的其他实例相矛盾,例如移动std::vector容器的位置通常以某种方式重置(在向量的情况下保证之后为空)以使其"无效",即使其中包含的对象也是如此他们自己已经离开了.这个或潜在的用例是否有任何理由支持,例如可能试图模仿相同类型的非可选版本的行为?
假设我有一个管理指向内部缓冲区的指针的类:
class Foo
{
public:
Foo();
...
private:
std::vector<unsigned char> m_buffer;
unsigned char* m_pointer;
};
Foo::Foo()
{
m_buffer.resize(100);
m_pointer = &m_buffer[0];
}
Run Code Online (Sandbox Code Playgroud)
现在,假设我也正确实现了3规则的东西,包括复制内部缓冲区的复制构造函数,然后将指针重新分配给内部缓冲区的新副本:
Foo::Foo(const Foo& f)
{
m_buffer = f.m_buffer;
m_pointer = &m_buffer[0];
}
Run Code Online (Sandbox Code Playgroud)
如果我还实现了移动语义,那么只复制指针并移动缓冲区是否安全?
Foo::Foo(Foo&& f) : m_buffer(std::move(f.m_buffer)), m_pointer(f.m_pointer)
{ }
Run Code Online (Sandbox Code Playgroud)
在实践中,我知道这应该工作,因为std::vector移动构造函数只是移动内部指针 - 它实际上并没有重新分配任何东西,所以m_pointer仍然指向一个有效的地址.但是,我不确定标准是否可以保证这种行为.std::vector移动语义是否保证不会发生重新分配,因此向量的所有指针/迭代器都是有效的?
考虑以下功能:
std::vector<int> pushIt(std::vector<int> v){
v.push_back(10);
return v;
}
int main(){
std::vector<int> vec;
vec = pushIt(std::move(vec));
}
Run Code Online (Sandbox Code Playgroud)
我的假设是向量移动到函数中,修改并移回其原始位置.这应该导致类似的行为,因为它作为非const引用传递.这似乎是非常有效的行为,但同事担心未定义的行为.这里有什么我想念的吗?
我想这样做是因为当前的功能
void currentPushIt(std::vector<int>& v){
v.push_back(10);
}
Run Code Online (Sandbox Code Playgroud)
在代码审查中导致了很多问题,因为人们忽略了一个无辜的调用currentPushIt(v)会使迭代器失效的事实.让他们写作v=pushIt(std::move(v))应该唤醒他们他们不会犯同样的错误.
我试图找出 C++ 11 标准是否允许std::vector释放接收移动分配的资源。
我给你举个例子:
std::vector<int> a {1, 2};
std::vector<int> b {3, 4};
a = std::move(b);
Run Code Online (Sandbox Code Playgroud)
现在我所期望的是,a和b都包装了一个常规数组(通常)并且它们有一个指向它的指针,即a_ptrand b_ptr。现在我认为除其他外的移动分配确实如此a_ptr = b_ptr。标准是否保证在a_ptr此之前或通过此释放内存?
作为这两个问题的具体子句:
人们不禁要问:是合法的调用.clear(),.chrink_to_fit(),.empty()在移动,从std::vector给它分配一个新的载体之前?我可以问一下,push_back()但我不知道会给出什么,因为依靠从向量移动的空是不安全的.
显而易见的是,破坏以及从新载体分配是合法的.
std::vector<int> fs = getVec();
giveVecs(std::move(fs));
fs.empty(); // 1.?
fs.size(); // 2.?
fs.shrink_to_fit(); // 3.?
fs.clear(); // //4. ?
fs = {} ; // 5. Should be fine, but weird when we have .clear()
fs.push_back(1); //
Run Code Online (Sandbox Code Playgroud)
编辑:
我应该澄清,有些操作确实有前提条件:因此,(正如你可以在其他问题中看到的那样),并非所有操作在移动后都是合法的.
因此,我的问题可以这样重述:三种操作中的任何一种都有任何先决条件吗?问题的一个原因可能是与从对象移动的分配器相关的精细打印.