Search code examples
c++templatesambiguous

How to resolve ambiguity in template base class template method?


I am trying to figure out how to resolve an ambiguity problem with function names in base classes.

#include <type_traits>

template <typename T, typename PARENT>
class BaseA 
{
public:
  BaseA(PARENT& p) : _parent(p) {}
  
public:
  template <typename P_ = PARENT>
  auto& parent() {
   if constexpr (std::is_same_v<P_, PARENT>) {
     return _parent;
   } else {
     return _parent.template parent<P_>();
   }
  }
  
private:
  PARENT& _parent;
};

class AbstractBaseB {
  
};

class BaseB : public AbstractBaseB
{
public:
  AbstractBaseB* parent() { return _parent; }
  
private:
 AbstractBaseB* _parent;
};

class Z {
public:
  
  void foo() {}
};

class Y : public BaseA<Y, Z>, public BaseB
{
public:
    Y(Z& z) : BaseA(z) {
        
    }
    
    void foo() {}
};

class X : public BaseA<X, Y>, public BaseB
{
public:
    X(Y& y) : BaseA(y) {
        //This will compile
        BaseA::parent().foo();

        //This will NOT compile
        BaseA::parent<Z>().foo();
    }
};

int main()
{
  Z z;
  Y y(z);
  X x(y);
}

This is a very specific/odd use case, so I have a working example here:

https://cppinsights.io/s/08afbad9

To get it to compile, just comment out line 58. With 58 enabled, this is where I get the ambiguity which is due to line 16:

return _parent.template parent<P_>();

Since _parent is of a different type than this instance of the BaseA template, I can't just do:

return _parent.template BaseA::parent<P_>();

like I did on line 57.

How do I go about fixing this?

For those who ask, the purpose of the templated parent method is to get the "Nth" nested parent without having to do something like parent().parent().parent()


Solution

  • If you want member function (templates) of the same name to be considered from multiple base classes you need to explicitly import them into the derived class scope:

    class Y : public BaseA<Y, Z>, public BaseB
    {
    public:
        /*...*/
    
        using BaseA::parent;
        using BaseB::parent;
    };