重载抽象运算符 = 时出现 Clang 链接器错误

Posted

技术标签:

【中文标题】重载抽象运算符 = 时出现 Clang 链接器错误【英文标题】:Clang linker error when overloading abstract operator= 【发布时间】:2016-03-28 11:11:47 【问题描述】:

VisualStudio 2013 编译器可以很好地处理以下代码,但是 clang 5.0 和 6.2 给我一个链接器错误:

#include <memory>

using namespace::std;

class IBase

public:
    virtual IBase& operator=(const IBase& other) = 0;
;

class Base : virtual public IBase

public:
    Base& operator=(const IBase& other) override
    
        const Base& b = dynamic_cast<const Base&>(other);
        return *this = b;
    

    virtual Base& operator=(const Base& other)
    
        return *this;
    
;

class IDerived : virtual public IBase

;

class Derived : public IDerived, public Base

public:
    using Base::operator=;

;

int main(int argc, const char * argv[]) 
    shared_ptr<Derived> d1 = make_shared<Derived>();
    shared_ptr<Derived> d2 = make_shared<Derived>();
    *d2 = *d1;

这是构建日志输出:

Ld /Users/Jennifer/Library/Developer/Xcode/DerivedData/Operator-bjjgcoxcziyegjgmazknrandutqz/Build/Products/Debug/Oper normal x86_64
    cd /Users/Jennifer/Documents/Operator
    export MACOSX_DEPLOYMENT_TARGET=10.9
    /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ -arch x86_64 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk -L/Users/Jennifer/Library/Developer/Xcode/DerivedData/Operator-bjjgcoxcziyegjgmazknrandutqz/Build/Products/Debug -F/Users/Jennifer/Library/Developer/Xcode/DerivedData/Operator-bjjgcoxcziyegjgmazknrandutqz/Build/Products/Debug -filelist /Users/Jennifer/Library/Developer/Xcode/DerivedData/Operator-bjjgcoxcziyegjgmazknrandutqz/Build/Intermediates/Operator.build/Debug/Oper.build/Objects-normal/x86_64/Oper.LinkFileList -mmacosx-version-min=10.9 -stdlib=libc++ -Xlinker -dependency_info -Xlinker /Users/Jennifer/Library/Developer/Xcode/DerivedData/Operator-bjjgcoxcziyegjgmazknrandutqz/Build/Intermediates/Operator.build/Debug/Oper.build/Objects-normal/x86_64/Oper_dependency_info.dat -o /Users/Jennifer/Library/Developer/Xcode/DerivedData/Operator-bjjgcoxcziyegjgmazknrandutqz/Build/Products/Debug/Oper

Undefined symbols for architecture x86_64:
  "IBase::operator=(IBase const&)", referenced from:
      IDerived::operator=(IDerived const&) in main.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

IBase::operator=(IBase const&amp;)Base 中定义,Derived 继承自 Derived,因此它应该Derived 定义,而不是被默认分配覆盖操作员。

我找到的一个解决方案是删除 IBase::operator= 方法,但这并不理想,因为它是任何继承类都需要实现的方法。

有谁知道区别是什么以及如何解决?如果可能,我想保留IBase::operator= 方法。

【问题讨论】:

【参考方案1】:

问题在于 using-declaration 不能算作用户声明的赋值运算符 [namespace.udecl]:

4 - [...] 如果 从基类带入派生类范围的赋值运算符具有复制/移动的签名 派生类的赋值运算符(12.8),使用声明本身不会抑制隐式 派生类赋值运算符的声明 [...]

(无论如何,using Base::operator= 为您提供参数类型为 Base const&amp; 的赋值运算符,它不是符合复制赋值运算符的参数类型之一[class.copy]/ 17 - TT&amp;T const&amp; 等)

因为Derived 没有用户声明的复制赋值运算符,所以会自动生成一个,最终调用IDerived::operator=,后者调用IBase::operator=。请注意,自动生成的复制赋值运算符调用忽略虚拟覆盖的子对象复制赋值运算符:

每个子对象的分配方式 适合其类型:

如果子对象是类类型,就好像通过调用operator= 将子对象作为对象表达式 和 x 的相应子对象作为单个函数参数(好像通过显式限定;那 是,忽略更多派生类中任何可能的虚拟覆盖函数); [...]

解决办法是这样写:

    Base& operator=(Derived const& other)  return Base::operator=(other); 

请注意,MSVC 2015 拒绝您的代码,但可以使用上述修复:

main.cpp(36): warning C4250: 'Derived': inherits 'Base::Base::operator =' via dominance
main.cpp(14): note: see declaration of 'Base::operator ='
main.obj : error LNK2019: unresolved external symbol "public: virtual class IBase & __thiscall IBase::operator=(class IBase const &)" (??4IBase@@UAEAAV0@ABV0@@Z) referenced in function "public: class IDerived & __thiscall IDerived::operator=(class IDerived const &)" (??4IDerived@@QAEAAV0@ABV0@@Z)
main.exe : fatal error LNK1120: 1 unresolved externals

【讨论】:

关于答案中的第一段,可能值得澄清的是,即使 using 声明引入了具有正确参数类型的声明,它仍然不会抑制复制赋值的隐式声明运算符。

以上是关于重载抽象运算符 = 时出现 Clang 链接器错误的主要内容,如果未能解决你的问题,请参考以下文章

在 Python 中使用重载加法运算符时出现内存错误

RestKit 链接器错误

为 HTML 5 编译 Minko 引擎 SDK 时出现 clang++ 错误

实例化模板类时出现链接器错误

升级到 XCTest 时出现链接器错误

包含omniture库时出现链接器错误