从函数返回STL对象而不触发move
Return STL objects from function without triggering move
假设有一个函数返回任何本地对象,实现移动语义,例如任何STL容器,如std::vector
, std::string
等。例如:
std::vector<int> return_vector(void)
{
std::vector<int> tmp {1,2,3,4,5};
return tmp;
}
默认情况下,tmp
将被视为右值,tmp
将被移动(或进行返回值优化)。
问题是如何手动覆盖和避免c++ 11的默认行为和执行复制构造函数,而不是移动到这里?一种解决方案可能是为std::vector
实现一个禁用move语义的包装器类,但这似乎不是一个好的选择。
这样做的原因之一可能是调用者在另一个程序集中,如果运行时库是静态链接的,则会有两个堆。由于跨dll边界分配/删除内存,移动将导致内存断言。
问题是如何避免c++ 11的默认行为和执行复制构造函数,而不是移动到这里?
这不是默认行为。在这种情况下,默认行为是省略副本。这一举动只会在不实施NRVO的不太可能的情况下发生。
简短的回答是,你不能让return f;
不移动。当您在c++中返回时,省略是默认的,如果不是,则移动它,如果不是,则复制它。如果您使用非平凡语句——甚至true?v:v
或static_cast<whatever const&>(v)
——它将阻止自动移动并强制复制。但那帮不了你。
逃避行动对你没有帮助。返回对象仍然在函数内创建,并由调用代码处理。
现在,并不是所有的都失去了。您可以通过使用头文件(存在于客户端代码中)进行分配和dll安全接口(到实现)来避免这种情况。
这里我设计了一个sink
类型,它批量地吸收T
类型的数据。然后调用带有pvoid的函数指针,就完成了。
template<class T>
struct sink {
void* s;
void(*)(void*, T const*, T const*) f;
void operator()(T const& t)const{ f(s, &t, (&t)+1); }
void operator()(std::initializer_list<T> il) {
f(s, il.begin(), il.end());
}
};
template<class T, class A>>
sink<T> vector_sink( std::vector<T, A>& out ) {
return {&out, +[](void* s, T const* b, T const* e){
auto* pout = static_cast<std::vector<T,A>*>(s);
pout->insert( pout->end(), b, e );
}};
}
现在,从DLL中导出:
void make_data(sink<int> s) {
s({1,2,3,4,5});
}
头文件中的,暴露:
void make_data(sink<int> s);
std::vector<int> return_vector() {
std::vector<int> r;
make_data( vector_sink(r) );
}
,现在vector
完全存在于DLL的客户端代码中。只有一个标准的布局类(由2个指针组成)跨越了DLL的障碍。
更高级的sink
可以通过添加一个新函数(用于move-data-in)来区分左值和右值。但是,如果这是为了跨越DLL边界,那么这似乎是不明智的。
处理"返回"一个向量。要接受一个矢量(不附加),我建议编写array_view<int>
,它包含两个int*
s:类似地,标准布局,可以非常安全地跨越DLL边界。
对引用执行static_cast即可(例如使用可移动类C,可以是vector)
转换为左值引用禁用移动和NRVO
C f() {
C c;
return static_cast<C&>(c);
}
转换为右值引用只禁用NRVO
C f() {
C c;
return static_cast<C&&>(c);
}
- 返回一个带有 std::move 的对象并链接函数
- 当 std::move 与 C 样式数组或不移动对象时会发生什么
- 如何在没有 std::move 的情况下移动临时对象
- 在返回语句中构造对象时,std::move() 是否有助于或阻止 RVO?
- 为什么 std::move 适用于常量对象
- 更改对象的类:可以使用 std::move 吗?
- 如果对象在同一层次结构中,-Wreturn-std-move clang 警告是否正确
- 为什么将临时对象作为参数传递需要 std::move?
- 在 std::move 之后使用对象不会导致编译错误
- 是否可以将 std::move 对象移出函数?(C++11).
- 将只有move构造函数的对象推回到向量
- std::使用了move,调用了move构造函数,但对象仍然有效
- 如果在没有move构造函数的情况下移动对象,会发生什么
- 为什么我们可以在const对象上使用std::move
- C++ 返回指针与返回带有 std::move 的本地对象
- 使用 "new ClassType(std::move(/*class_object*/))" 在自由存储中构造对象
- 为什么在 const 对象上调用 std::move 在传递给另一个对象时会调用复制构造函数
- 当将昂贵的对象存储在一对中时,是否有必要进行 std::move
- 有没有办法使用 move 而不是复制语义将函数返回值(对象)包装在 Python 中
- copy-/move-constructors的奇怪行为以及如何返回大对象?