0
votes

Why does the following code not compile and what can I do about it? Shouldn't it find the pure abstract method declared in the base class?

#include <string>

using namespace std::string_literals;

struct A
{
    virtual void func1(int) = 0;
    virtual void func1(std::string) = 0;
};

struct B : A
{
    void func1(int) override
    {
        func1("bla"s);
    }
};

struct C : B
{
    void func1(std::string) override
    {
    }
};

error C2664: 'void B::func1(int)': cannot convert argument 1 from 'std::string' to 'int'

Thanks in advance, Martin

Edit about the linker error!

If have three files:

file test.h

#include <string>

using namespace std::string_literals;

struct A
{
    virtual void func1(int) = 0;
    virtual void func1(std::string) = 0;
};

struct B : A
{
    void func1(int) override
    {
        A::func1("bla"s);
    }
};

struct C : B
{
    void func1(std::string) override;
};

file test.cpp

void C::func1(std::string)
{

}

file main.cpp

int main()
{
    C c;

    return 0;
}

This leads to

Error LNK2019 unresolved external symbol ""public: virtual void __thiscall A::func1(class std::basic_string,class std::allocator >)" (?func1@A@@UAEXV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z)" in Funktion ""public: virtual void __thiscall B::func1(int)"

I'm using visual C++ v140.

It works with using A::func1;. What is the difference?

2
A::func1("bla"s); or using A::func1; inside of B definition so both overloads could be found by name lookup inside of Buser7860670
B is missing void func1(std::string) override = 0;Eljay

2 Answers

4
votes

B's implementation of func1 hides the other two overloads from A. You can import them back into B's interface by:

struct B
{
    using A::func1;
    /* ... */
};

In general, you can alternatively call a base class' overloads directly:

void /*B::*/func1(int) override
{
    A::func1("bla"s);
}

This will call exactly the version of the base class, though, not an overriding variant of, but in given case, A's variant of is pure virtual (i. e. does not exist), and thus we cannot apply this approach here.

2
votes

You have the same problem with C - this will not compile:

C c;
c.func1(123);

Name lookup happens before overload resolution, and lookup stops in the scope where the first match is found.

You can introduce all the overloads into the scope of a derived class using using:

struct B : A
{
    using A::func1;  // Make the string overload available trough B's interface.
    void func1(int) override
    {
        func1("bla"s);
    }
};

struct C : B
{
    using B::func1; // Make the int overload available through C's interface.
    void func1(std::string) override
    {
    }
};

Your code would also compile if you called A::func1(std::string) and provided an implementation for it;

struct A
{
    virtual void func1(int) = 0;
    virtual void func1(std::string) = 0;
};

// Yes, you can provide an implementation for a pure virtual function.
void A::func1(std::string) {}

struct B : A
{
    void func1(int) override
    {
        A::func1("bla"s);
    }
};

but this code - which you probably want to work - would not:

void foo(B* b)
{
    b->func1("blargh"s);
}