3

Consider the following code:


template<typename T>
struct Ambiguous_C {
    void func(T);
};

struct Ambiguous_F {
    template<typename T>
    void func(T);
};

struct Conflicted_F : public Ambiguous_F{
    void test (){
        func(1);
        func<int>(1);
        func(1.1f);
        func<float>(1.1f);
    }
};
struct Conflicted_C : public Ambiguous_C<int>, Ambiguous_C<float>{
    void test (){
        //func(1);                         /* Error */
        Ambiguous_C<int>::func(1);
        //func(1.1f);                      /* Error */
        Ambiguous_C<float>::func(1.1f);
    }
};
Member 'func' found in multiple base classes of different types

This was compiled using clang 20.1.2

I expected the function calls in Conflicted_C::test() to resolve the overloaded function call based on the function signature, just like the code shown in Conflicted_F::test(). Are there additional considerations taken into account with regards to templated classes that I am not aware of?

Is there a way to implement a templated base class/ method, such that the explicit base class Ambiguous_C:: in front of a call to func can be omitted?

I noticed this gives the same error:


void f_func(int);
void f_func(float);

struct Ambiguous_P1 {
    void func(int);
};

struct Ambiguous_P2 {
    void func(float);
};

struct Conflicted_P : public Ambiguous_P1, Ambiguous_P2{
    void test() {
        func(1);             /* Error */
        func(1.1f);          /* Error */

        f_func(1);           /* Works */
        f_func(1.1f);        /* Works */
    }
};

But the question remains the same. Why is the resolution of this ambiguous call not possible based on function signature?

1
  • 2
    You might look at unqualified_lookup for all details. Commented Apr 1 at 9:05

2 Answers 2

8

You have to add some using:

struct Conflicted_C : public Ambiguous_C<int>, Ambiguous_C<float>{
    using Ambiguous_C<int>::func;
    using Ambiguous_C<float>::func;
    void test (){
        func(1);                         /* OK */
        Ambiguous_C<int>::func(1);
        func(1.1f);                      /* OK */
        Ambiguous_C<float>::func(1.1f);
    }
};

Demo

Sign up to request clarification or add additional context in comments.

Comments

2

In addition to @Jarod42 you can also generalize the struct further by making the using based on a template.

template<typename X, typename Y>
struct Conflicted_C : public Ambiguous_C<X>, Ambiguous_C<Y>{
    using Ambiguous_C<X>::func;
    using Ambiguous_C<Y>::func;
    void test (){
        func(1);                         /* OK */
        Ambiguous_C<X>::func(1);
        func(1.1f);                      /* OK */
        Ambiguous_C<Y>::func(1.1f);
    }
};

This does have the effect of declaring your Conflicted_C class with the types are you willing to support:

int main() {
    Conflicted_C<int, float> conflict;
    conflict.test();
}

Demo

Using template parameter packing this example could be extended to allow an arbitrary number of template parameters if needed.

2 Comments

Not that the template part has much to do with OPs problem, but if you want to make it generic, there's no point in limiting it to two base classes only. example
Although it was not part of the original question, it would have been my follow up. I did have to make use of this in the end. Thank you both!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.