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 是不是支持移动构造函数的主要内容,如果未能解决你的问题,请参考以下文章

Protocol buffers 介绍

Java使用Protocol Buffers入门四步骤

理解netty对protocol buffers的编码解码

Google Protocol Buffers 入门

Dart-RPC:使用 Protocol Buffers 序列化而不是 JSON

Protocol Buffers(Objective-C)踩坑指南