6

VisualStudio 2013 kompilator obsługuje następujący kod dobrze, ale Clang 5.0 i 6.2 daje mi błąd linkera:błąd linkera Clang gdy przeciążenia streszczenie operatora =

#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; 
} 

oto wyjście dziennika budowy:

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&) jest zdefiniowana w Base które Derived dziedziczy i Derived jest using Base::operator= więc powinna być określona dla Derived, nie zastąpiony przez domyślny operator przypisania.

Jednym z rozwiązań, które znalazłem, było usunięcie metody IBase::operator=, ale nie jest to idealne rozwiązanie, ponieważ jest to metoda, którą powinna zaimplementować każda dziedzicząca klasa.

Czy ktoś wie, co to jest różnica i jak to naprawić? Jeśli to możliwe, chciałbym zachować metodę IBase::operator=.

Odpowiedz

5

Problemem jest to, że przy użyciu deklaracja nie liczy się jako zadeklarowana przez użytkownika operatora przypisania [namespace.udecl]:

4 - [...] If an assignment operator brought from a base class into a derived class scope has the signature of a copy/move assignment operator for the derived class (12.8), the using-declaration does not by itself suppress the implicit declaration of the derived class assignment operator [...]

(W każdym razie using Base::operator= daje operator przypisania z parametrem Base const& typu, który nie jest jednym z typów parametrów zakwalifikowanych jako operator przypisania kopii [class.copy]/17 - T, T&, T const& etc.)

Ponieważ Derived nie ma zadeklarowanego przez użytkownika operatora przypisania kopiowania, jeden jest generowany automatycznie, co kończy się wywołaniem IDerived::operator=, który wywołuje IBase::operator=. Należy pamiętać, że automatycznie generowane operatory przypisania kopia zadzwonić podobiektu operatory przypisania kopia ignorowaniu wirtualnych przesłonięcia:

Each subobject is assigned in the manner appropriate to its type:

  • if the subobject is of class type, as if by a call to operator= with the subobject as the object expression and the corresponding subobject of x as a single function argument (as if by explicit qualification; that is, ignoring any possible virtual overriding functions in more derived classes); [...]

fix byłoby napisać:

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

Zauważ, że MSVC 2015 odrzuca swój kod, ale działa z powyższych fix:

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 &)" ([email protected]@[email protected]@@Z) referenced in function "public: class IDerived & __thiscall IDerived::operator=(class IDerived const &)" ([email protected]@[email protected]@@Z) 
main.exe : fatal error LNK1120: 1 unresolved externals 
+0

Odnośnie do pierwszego akapitu w odpowiedzi, warto wyjaśnić, że nawet jeśli deklaracja użycia przyniosła deklarację z właściwym parametrem e, to nadal nie powstrzymałoby domyślnej deklaracji operatora przydziału kopiowania. – bogdan

+0

@bogdan dzięki, zapomniałem tej części. – ecatmur