Search code examples
c++operator-overloadingclanglibtooling

Why does clang create these implicit methods when a class contains a virtual method?


I'm working on a tool based on clang's AST, and I'm curious to know why clang works this way.

Here's my input. I have a very simple class that is defined like this:

class Foo {
    int foo();
};

Then in my RecursiveASTVisitor, I have code that looks like this:

bool MyASTVisitor::VisitCXXRecordDecl(clang::CXXRecordDecl *D) {
    for (auto const & method : D->methods()) {
        llvm::outs() << method->getQualifiedNameAsString() << "(";
        for (auto const & param : method->params())
            llvm::outs() << param->getType().getAsString() << ", ";
        llvm::outs() << ")";
        if (method->isImplicit())
            llvm::outs() << " [implicit]";
        llvm::outs() << "\n";
    }
    return true;
}

All this does is spit out the list of methods defined in all the classes that are visited. The output for this is as we'd expect:

Foo::foo()

Now, let's make a small change to our Foo class. Let's make the foo() method virtual:

class Foo {
    virtual int foo();
};

Now my output changes:

Foo::foo()
Foo::operator=(const class Foo &, ) [implicit]
Foo::~Foo() [implicit]

My question is, why does adding a virtual method to a class cause clang to create an implicit assignment operator and destructor? If I add --std=c++11, it creates an implicit move-assignment operator as well. Is this an implementation detail of clang, or is it part of the C++ standard?


Solution

  • Turns out that I should just read the clang source code. SemaDeclCXX.cpp has a method called Sema::AddImplicitlyDeclaredMembersToClass. There's a comment about why it declares the implicit copy assignment:

    if (!ClassDecl->hasUserDeclaredCopyAssignment()) {
      ++ASTContext::NumImplicitCopyAssignmentOperators;
    
      // If we have a dynamic class, then the copy assignment operator may be
      // virtual, so we have to declare it immediately. This ensures that, e.g.,
      // it shows up in the right place in the vtable and that we diagnose
      // problems with the implicit exception specification.
      if (ClassDecl->isDynamicClass() ||
          ClassDecl->needsOverloadResolutionForCopyAssignment())
        DeclareImplicitCopyAssignment(ClassDecl);
    }
    

    So it does this in order to ensure that the implicitly defined methods, that may be virtual, end up in the right place in the vtable.