Protocol Buffers 是不是支持移动构造函数
Posted
技术标签:
【中文标题】Protocol Buffers 是不是支持移动构造函数【英文标题】:Does Protocol Buffers support move constructorProtocol Buffers 是否支持移动构造函数 【发布时间】:2015-05-18 13:50:42 【问题描述】:我检查了move constructor 规范和Message constructor 来源,但没有找到。
如果没有,有人知道添加它的计划吗?
我正在使用proto3
语法,编写一个库并考虑通过 value 与 unique_ptr 之间的返回。
【问题讨论】:
当这是 discussed in the mailing list 时,有人附加了他们编写的 protoc 生成器插件以添加移动构造函数支持。他们称它为 protoc-gen-cxx11,但不幸的是我在任何地方都找不到它。 【参考方案1】:根据https://github.com/google/protobuf/issues/2791,这将在 Protobuf 版本 3.4.0 中得到支持。
【讨论】:
【参考方案2】:如果您尝试使用赋值运算符,RVO 将进行优化以防止出现额外的副本。
// RVO will bring the return value to a without using copy constructor.
SomeMessage a = SomeFooWithMessageReturned();
如果您想使用std::move
将左值移动到列表/子消息等中,请尝试使用ConcreteMessage::Swap
方法。交换的物品将毫无用处。
// Non-copy usage.
somemessage.add_somerepeated_message()->Swap(&a);
somemessage.mutable_somesinglar_message()->Swap(&a);
// With message copying
somemessage.add_somerepeated_message()->CopyFrom(a);
*somemessage.mutable_somesinglar_message() = a;
【讨论】:
非常感谢您关注Swap
方法。 protobuf 对象的移动分配可以使用下一个函数来模拟:template <typename Protobuf> static void pb_move_assign(Protobuf &to, Protobuf &&from) from.Swap(&to); from.Clear();
@anton_rh Clear() 不是必需的;不能保证 from
对象在移动后会处于什么状态(除非它是合法状态),例如一个简短的 std::string 很可能在移动后具有其原始值。所以你真的不如直接调用 Swap() 而不是包装函数。【参考方案3】:
从 2.6.1 版开始,C++ protobuf 编译器仅生成复制构造函数和复制赋值运算符。但是,如果您的编译器支持return value optimization(并且满足它的条件),则无论如何都不会调用复制构造函数。
您可以将一些打印语句添加到消息的复制构造函数的生成代码中,以查看它们是否真的被调用。您也可以通过编写一个 protoc 插件来做到这一点,因此它在 protoc 调用之间保持不变。
【讨论】:
实际上很难确保在您期望的所有情况下都使用 RVO,因为编译器不需要使用它,而且编译器并不总是很聪明地决定是否可以使用它.您基本上必须确保您的函数只有 一个 return 语句(返回一个局部变量)。在实践中还有很多其他情况需要类似移动的语义,尽管其中一些可以通过仔细使用 swap() 来解决。 显然,如果您需要大型消息对象向量之类的东西,RVO 将无济于事。一种解决方法是对消息使用 unique_ptr 的向量,但这是一个符号上的烦恼,这意味着您的源函数必须返回 unique_ptrs 以避免在其他地方复制,因此该解决方法可能会感染大量代码。 @JimOldfield 您可以使用 protobuf 容器google::protobuf::RepeatedPtrField<MyMessage>
,而不是 std::vector<std::unique_ptr<MyMessage>>
。此类用于重复字段,但也可以用于其他目的。它在内部使用指针,但迭代器直接指向消息,因此您不需要双重取消引用。根据 Daniel Schepler 的回答,从 protobuf 3.4 开始,这两种解决方法都不是必需的。
3.6.1 的注释与 protobuf changlelog 不同,它在 3.4 中添加了对大多数对象的移动,在 3.5 中添加了一些(数组类型),并在 3.6 中关闭了对 CXX 功能的防护@987654322 @ 特别是,3.4 具有“当 C++11 可用时引入以下 C++11 特性”: - 将移动构造函数和移动赋值引入消息 - 重复字段构造函数现在采用 std::initializer_list以上是关于Protocol Buffers 是不是支持移动构造函数的主要内容,如果未能解决你的问题,请参考以下文章