-1

maybe this is a FAQ, but all exisiting questions I found were referring to methods with identical signature, i.e. real overrides.

My problem boiled down to a minimal example:

class A
{
public:
    enum class Enumeration {Value_1, Value_2};
    void SetValue(Enumeration e)  //!! this is what it is about
    {
        m_member = e;
    }

    Enumeration m_member;
};

class B
{
public:
    void SetValue(std::string value) // same method name, different parameter type -> different method signature
    {
        m_stringMember = value;
    }

    std::string m_stringMember;
};

class C : public A, B // inheriting both SetValue() methods
{

};

int main(int argc, char **)
{
    C test;
    test.SetValue(A::Enumeration::Value_1); // C2385 - ambiguous access
    test.SetValue("Test"); // C2385 - ambiguous access
}

Compiler: VC++ 15

The call to both test.SetValue() functions gives me a C2385:

error C2385: ambiguous access of 'SetValue'

note: could be the 'SetValue' in base 'A'

note: or could be the 'SetValue' in base 'B'

Regarding the error message, I object. It "could not" be the SetValue in the other base class because the parameter type mismatches. It should be clear to the compiler which method matches the call.

Obviously (verified), overloading works fine if I put both methods into class A and instantiate A:

class A
{
public:
    enum class Enumeration {Value_1, Value_2};
    void SetValue(Enumeration e)
    {
        m_member = e;
    }

    void SetValue(std::string value)
    {
        m_stringMember = value;
    }


    Enumeration m_member;
    std::string m_stringMember;
};

int main(int argc, char **)
{
    A test;
    test.SetValue(A::Enumeration::Value_1);
    test.SetValue("Test");
}

What makes the difference here? How do these two method declarations interfere? Is there a way of overcoming this which does not require changing the name of SetValue() in one of C's base classes?

Community
  • 1
  • 1
Markus Sabin
  • 3,916
  • 14
  • 32
  • @churill -- that's not the case here. Neither function hides the other. They are not overloads, because they are defined in different scopes. – Pete Becker Mar 20 '20 at 14:05
  • 1
    @PeteBecker Did you read through the duplicate and especially the first and second answers? I think it's the same problem here, which is called name hiding. Also [this question](https://stackoverflow.com/questions/32946364/c-inheritance-and-name-hiding), which is basically the same, was also marked as duplicate of the one above. – Lukas-T Mar 20 '20 at 14:08
  • @PeteBecker: you are right. Yet I suspect that the rationale explained in the post that churill linked applies here too. There is first a *name* resolution which is ambiguous, and it occurs before the true overload resolution - which would not be. – Serge Ballesta Mar 20 '20 at 14:10
  • @churill -- again: it is **not** name hiding. Neither of the two classes that define `SetValue` is a base of the other. Both functions are visible in `C`. – Pete Becker Mar 20 '20 at 14:11
  • 1
    @PeteBecker Ok, you are right, I was confusing terminology here. But the second answer (from Drew Hall), summarized by Serge Ballesta above, explains this question quite well. . – Lukas-T Mar 20 '20 at 14:17

1 Answers1

1

This has to be a duplicate, but I can't find it.

Overloading applies to functions with the same name defined in the same scope. In the code in the question, the two functions are defined in different scopes, one in class A and one in class B, so they do not overload, and the compiler is not allowed to pick one or the other. So the call is ambiguous.

The way to resolve this is to tell the compiler which one you want. From outside it's ugly: test.B::SetValue("Test");, if I remember correctly.

The reason for this is maintainability. Suppose that the class A in the example came from a third-party library and did not have a function named SetValue. In the code in the question, test.SetValue("test") would be fine: it calls B::SetValue(std::string), converting the string literal into a std::string. Now you install a new version of that third-party library, and A now has a new member function, A::SetValue(const char*). If the compiler applied overload resolution to those two SetValue functions, it would call the new one, and the behavior of your previous code would change: it would call A::SetValue instead of B::SetValue. That makes code fragile.

Since there seems to be a lot of confusion here: this is not name hiding. The usual example of name hiding occurs when a base class defines a name, and a class derived from that base also defines a name. The one in the base is said to be hidden; formally, the compiler simply stops looking when it sees the one in the derived class. The same thing applies when a class defines a name that's the same as a name in global scope; the compiler doesn't look outside the class after it's found the name inside the class.

Pete Becker
  • 74,985
  • 8
  • 76
  • 165
  • Yes it was a duplicate, but it was too hard to find it because of tons of duplicates with the topic of ambiguous overloads methods with the same signature. Anyway, thank you very much for your good explanation Pete – Markus Sabin Mar 23 '20 at 06:48
  • Checked the other sufggested answers and tried to add in class C: using A::SetValue; using B::SetValue -> works. This is a good solution for me. – Markus Sabin Mar 23 '20 at 09:50