【问题标题】:Does adding a move constructor break binary compatibility?添加移动构造函数会破坏二进制兼容性吗?
【发布时间】:2015-07-06 23:29:45
【问题描述】:

如果我将移动构造函数(或移动赋值运算符)添加到我的库中,我会破坏二进制兼容性吗?该添加会以任何方式破坏用户的代码吗?

class Foo {
public:
  Foo();
  Foo(Foo const&);
  Foo& operator=(Foo const&);

// new methods:
  Foo(Foo&&);
  Foo& operator=(Foo&&);
};

【问题讨论】:

  • 如果是的话,它会首先进入标准吗?
  • 这不是特定于编译器的吗?没有提到编译器。
  • 我们的库 (jsoncpp) 支持许多编译器。我很确定我们在二进制兼容性方面没问题,但也许存在移动语义的极端情况,它可能会以其他方式破坏现有的源代码。

标签: c++ c++11 move binary-compatibility


【解决方案1】:

在我看来,只要不添加成员或虚函数,就不会对二进制兼容性产生任何影响,因为对象的布局不会改变。

如果一个组件(比如共享库,Windows 上的 .dll 或 Linux 上的 .so)使用旧版本的库,那么它将复制对象的所有实例(甚至是右值),无论它是否被创建通过使用新库的组件(反之亦然)。

只要使用移动语义来提高性能,因此生成的移动对象的行为与复制的对象相同,应该没有问题。唯一的区别是由于对内存 [de] 分配和副本(等)的调用减少而提高了性能。如果移动操作用于产生不同的语义(移动的对象与复制的对象不同),那么所有的赌注都没有了,但我认为没有人会故意这样做(也许是为了工作安全)。

只要对象的二进制布局不改变,我看不出如何引入任何破坏。

【讨论】:

    【解决方案2】:

    它肯定会在一个方向上破坏二进制兼容性:针对新库编译的代码无法与旧库一起使用,因为链接时找不到移动构造函数。

    另一个方向更棘手。这通常不是什么大问题,但是代码可以至少观察到带有 SFINAE 技巧的新赋值运算符的存在,并且您最终会得到一个程序,其中某些部分认为该运算符存在,并且其他部分没有。如果相同的代码被编译两次(不同翻译单元中的相同模板实例化),这甚至可能导致 ODR 违规。而这些 ODR 违规可能会再次导致链接时错误。

    【讨论】:

    • 我认为可以安全地假设在编译代码时它使用相同版本的头文件和库。问题(据我所知)是在使用不同版本的库编译的二进制文件之间传递对象时会出现问题。例如,不同二进制文件的 ODR 是否正确?
    • @Motti 二进制“A”可能使用库“B”和库“C”,其中库“B”也使用库“C”。二进制“A”和库“B”可能会针对不同版本的库“C”编译,然后即使你只有一个程序,也会遇到我描述的问题。
    • 如果我明白你在说什么(禁止用户为自己的利益太爱管闲事)任何问题都会导致程序无法链接,并将通过链接到更高版本的图书馆。
    • @Motti 不会,它会导致程序无法链接,但也可能导致程序链接无误,并在运行时出现意外行为。
    • 怎么样?您能否扩展您的答案以包括解释?
    猜你喜欢
    • 2018-12-27
    • 2012-09-24
    • 2013-03-08
    • 2016-08-20
    • 2014-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多