简体   繁体   中英

Ambiguous call when inheriting multiple times from generic base class

I want to clone a data structure containing std::list s of several types and simultaneously move some iterators to point to the new std::list 's elements. For this, I created a generic type Translate<T> represents the mapping from std::list<T>::iterator s in the old list to those in the new list. I then have a new class inherit from Translate<T> for all the needed types T of my data structure.

Here's a simplified example (using just an identity function):

#include <list>

struct T1 {};
struct T2 {};

template<typename T>
class Translate {
    public:
    typename std::list<T>::iterator operator()(typename std::list<T>::iterator x) {
        return x; // more complex in the real world, but doesn't matter here
    }
};

int main() {
    std::list<T1> l1{};
    std::list<T2> l2{};

    class : public Translate<T1>, public Translate<T2> {} tr;
    tr(l1.begin());
}

This gives me the following compiler error:

mwe.cpp: In function ‘int main()’:
mwe.cpp:19:15: error: request for member ‘operator()’ is ambiguous
  tr(l1.begin());
               ^
mwe.cpp:9:34: note: candidates are: ‘typename std::__cxx11::list<T>::iterator Translate<T>::operator()(typename std::__cxx11::list<T>::iterator) [with T = T2; typename std::__cxx11::list<T>::iterator = std::_List_iterator<T2>]’
  typename std::list<T>::iterator operator()(typename std::list<T>::iterator x) {
                                  ^~~~~~~~
mwe.cpp:9:34: note:                 ‘typename std::__cxx11::list<T>::iterator Translate<T>::operator()(typename std::__cxx11::list<T>::iterator) [with T = T1; typename std::__cxx11::list<T>::iterator = std::_List_iterator<T1>]’

In what way is the call ambiguous? The std::list<T>::iterator s are not convertible to each other.

It works if I manually copy the implementation for each T into the child class, but that's exactly what is trying to be avoided when using generics and inheritance.

This code is ill-formed. If the operator() is not available in the derived class, then name lookup only considers the base class if there is exactly one base class. If there are multiple base classes, then none of the base classes are considered in name lookup.

As you've mentioned, you can copy the implementations of operator() into the derived class, and that works, but you can also bring both of the operator() names into the derived class with a using directive, like this:

class : public Translate<T1>, public Translate<T2> {
  public:
    using Translate<T1>::operator();
    using Translate<T2>::operator();
} tr;

Here's a demo .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM