添加移动构造函数会破坏二进制兼容性吗?

10

如果我在我的库中添加移动构造函数(或移动赋值运算符),那么会破坏二进制兼容性吗?这种添加是否可能以任何方式破坏用户的代码?

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

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

如果它是的话,它会首先进入标准里吗? - David Haim
这不是特定于编译器的吗?没有提到任何编译器。 - Quentin
1
我们的库(jsoncpp)支持许多编译器。我非常确定我们在二进制兼容性方面没问题,但也许有一些移动语义的边角情况可能会以某种其他方式破坏现有的源代码。 - cdunn2001
2个回答

1
在我看来,只要您不添加成员或虚函数,那么对象的布局不会改变,因此不应该对二进制兼容性产生任何影响。
如果一个组件(比如一个共享库,在Windows上是.dll,在Linux上是.so)使用旧版本的库,那么它将复制所有对象的实例(即使是右值),无论是由使用新库的组件创建的还是相反。
只要使用移动语义以提高性能,并且因此移动后的对象与复制的对象行为相同,就不应该有问题。唯一的区别是由于减少了对内存[de]分配和复制等操作的调用而导致的性能提高。如果使用移动操作来生成不同的语义(移动对象与复制对象不同),那么所有赌注都将失效,但我认为没有人会故意这样做(除了可能是为了工作保障)。
只要对象的二进制布局不改变,我不认为会引入任何破坏。

1

它确实会破坏二进制兼容性:针对你的新库编译的代码无法与旧版本一起使用,因为链接时找不到移动构造函数。

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


如果我理解你的意思是(除非用户过于好奇而产生问题),任何问题都会导致程序无法链接,并且通过链接到较新版本的库来解决。 - Motti
@Motti 除了一个错别字(现已修正),我已经尝试过解释了:如果同一模板被实例化两次,并且这些模板实例化不相同,那么在运行时,如果链接成功,几乎肯定会选择一个实例化:如果它们具有相同的名称,则通常不能同时存在。但是代码可能期望另一个实例化。 - user743382
抱歉,我今天可能特别迟钝。如果一个类型现在具有移动构造函数,并且在模板中使用了该类型,而该模板是由使用旧版本编译的代码调用的,则此代码可能会获得使用移动构造函数的模板实例化(该实例化链接到新库中)。这会导致意外行为吗?(假设移动构造函数具有常规语义并且没有执行任何恶意操作) - Motti
假设某个常见的公用程序库有这样一种(愚蠢的)模板函数:template <typename T> T f(T& t) { return T(std::move(t)); } 假设二进制文件“A”使用你库的旧版本“C”来实例化 f<X>,并得到复制构造函数。假设库“B”也实例化了带有你新库版本的f<X>,并得到移动构造函数。那么当二进制文件“A”与库“B”链接时会发生什么?你不能在同一个程序中有多个f<X>实例化,因此会选择一个版本,可能是二进制文件的版本。即使从库中调用该函数,也将调用该版本。 - user743382
@Motti(假设您没有收到任何错误消息,无论如何。)但是库“B”可能一直依赖于f的移动效果,并且完全有权这样做。 - user743382
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接