Home c++ Compilation error “no matching function for call” and unexplained “solution”

Compilation error “no matching function for call” and unexplained “solution”

Author

Date

Category

I am making a generic container template to store objects not only in memory and to work with ‘Range-based for loop’ (that is, in the style of for (auto i: items) ). The code is not finished yet, I want to deal with the error, I don’t understand something

I have a base class (template):

template & lt; class Key, class T & gt;
class IContainer
{
public:
  class IOuterIterator
  {
  public:
    IOuterIterator () {}
    virtual T operator * () {return T ();}
    virtual bool operator! = (const IOuterIterator & amp; other) {return false;}
  protected:
  private:
  };
  virtual IOuterIterator begin () = 0;
  virtual IOuterIterator end () = 0;
private:
};

some derived class Container

template & lt; class Key, class T, class IteratorKey & gt;
class Container: public IContainer & lt; Key, T & gt;
{
public:
  Container (): IContainer & lt; Key, T & gt; () {}
  class OuterIterator: public IContainer & lt; Key, T & gt; :: IOuterIterator
  {
  public:
    OuterIterator (Container & lt; Key, T, IteratorKey & gt; * container):
      IContainer & lt; Key, T & gt; :: IOuterIterator (),
      _container (container)
    {
    }
    T operator * () override
    {
      return _container- & gt; value (_iteratorKey);
    }
    bool operator! = (const typename IContainer & lt; Key, T & gt; :: IOuterIterator & amp; other) override {
      OuterIterator oit (_container);
      oit = ​​(OuterIterator) other; // ERROR HERE!
      return (_iteratorKey! = oit._iteratorKey);
    }
  private:
    // inner class objects don't have an implicit reference to an outer object.
Container & lt; Key, T, IteratorKey & gt; * _container;
    IteratorKey _iteratorKey;
  };
  typename IContainer & lt; Key, T & gt; :: IOuterIterator begin () {
    return OuterIterator (this); // TODO: set to begin
  }
  typename IContainer & lt; Key, T & gt; :: IOuterIterator end () {
    return OuterIterator (this); // TODO: set to the end
  }
protected:
  virtual T value (Key key, const T & amp; defaultValue) const = 0;
  virtual T value (IteratorKey ikey) = 0;
};

And the implication for the container with memory:

template & lt; class Key, class T & gt;
class MemoryContainer: public Container & lt; Key, T, Key & gt;
{
  T value (Key key, const T & amp; defaultValue) const override
  {
    return 0; // TODO:
  }
  T value (Key key) override
  {
    return 0; // TODO:
  }
};
// TODO: FileContainer, DBContainer, RemoteContainer

Call somewhere in the main:

MemoryContainer & lt; int, int & gt; container;

Error:

error: no matching function for call to 'Container & lt; int, int, int & gt; :: OuterIterator :: OuterIterator ( const IContainer & lt; int, int & gt; :: IOuterIterator & amp;) '
       oit = ​​(OuterIterator) other;
         ^
candidate: Container & lt; Key, T, IteratorKey & gt; :: OuterIterator :: OuterIterator (Container & lt; Key, T, IteratorKey & gt; *) [with Key = int; T = int; IteratorKey = int]
     OuterIterator (Container & lt; Key, T, IteratorKey & gt; * container):
     ^

The funny thing is that when I remove (comment) the begin () and end () functions from IContainer, the error inexplicably disappears (or rather, it appeared when I added these functions).

The whole code of the main.cpp file (for copy-paste):

# include & lt; QMap & gt;
template & lt; class Key, class T & gt;
class IContainer
{
public:
  class IOuterIterator
  {
  public:
    IOuterIterator () {}
    virtual T operator * () {return T ();}
    virtual bool operator! = (const IOuterIterator & amp; other) {return false;}
  protected:
  private:
  };
  virtual IOuterIterator begin () = 0;
  virtual IOuterIterator end () = 0;
private:
};
template & lt; class Key, class T, class IteratorKey & gt;
class Container: public IContainer & lt; Key, T & gt;
{
public:
  Container (): IContainer & lt; Key, T & gt; () {}
  class OuterIterator: public IContainer & lt; Key, T & gt; :: IOuterIterator
  {
  public:
    OuterIterator (Container & lt; Key, T, IteratorKey & gt; * container):
      IContainer & lt; Key, T & gt; :: IOuterIterator (),
      _container (container)
    {
    }
    T operator * () override
    {
      return _container- & gt; value (_iteratorKey);
    }
    bool operator! = (const typename IContainer & lt; Key, T & gt; :: IOuterIterator & amp; other) override {
      OuterIterator oit (_container);
      oit = ​​(OuterIterator) other;
      return (_iteratorKey! = oit._iteratorKey);
    }
  private:
    // inner class objects don't have an implicit reference to an outer object.
    Container & lt; Key, T, IteratorKey & gt; * _container;
    IteratorKey _iteratorKey;
  };
  typename IContainer & lt; Key, T & gt; :: IOuterIterator begin () {
    return OuterIterator (this); // TODO: set to begin
  }
  typename IContainer & lt; Key, T & gt; :: IOuterIterator end () {
    return OuterIterator (this); // TODO: set to the end
  }
protected:
virtual T value (Key key, const T & amp; defaultValue) const = 0;
  virtual T value (IteratorKey ikey) = 0;
private:
};
template & lt; class Key, class T & gt;
class MemoryContainer: public Container & lt; Key, T, Key & gt;
{
  T value (Key key, const T & amp; defaultValue) const override
  {
    return 0; // TODO:
  }
  T value (Key key) override
  {
    return 0; // TODO:
  }
};
// TODO: FileContainer, DBContainer, RemoteContainer
int main (int argc, char * argv [])
{
MemoryContainer & lt; int, int & gt; container;
IContainer & lt; int, int & gt; * _container1 = new MemoryContainer & lt; int, int & gt; ();
}

Answer 1, authority 100%

In fact, it’s not surprising and strange that this error appeared when you added begin and end to the base class – but that everything works without them.

Apparently, optimization miracles are to blame, which threw out non-working code.

Let’s take a closer look at this method – the real mistake is in it:

typename IContainer & lt; Key, T & gt; :: IOuterIterator begin () {
  return OuterIterator (this); // TODO: set to begin
}

Here the OuterIterator class is cast to its parent. But in C++, polymorphism is only available for references and pointers , not values! This code shouldn’t work without a cast operator.


Usually this problem is solved as follows – they introduce an intermediate link for which polymorphism is available.

That is, for example, the class IContainer :: iterator is introduced, which is a common “free-end” for all classes – and the abstract class IContainer :: IIterator , which hides implementation details .

The first class holds a pointer to the second:

class iterator {
std :: unique_ptr & lt; IIterator & gt; impl;
public:
 iterator (IIterator * impl): impl (impl) {}
 // ...
 T & amp; operator * () {return impl- & gt; current (); }
 T operator * () const {return impl- & gt; current (); }
}

However, this method can lead to a performance drawdown if the container type is well known – virtual calls are not free.

Therefore, in the final descendant class, it makes sense to duplicate the iterator so that it uses a specific implementation. This can be done using templates (aka static polymorphism):

template & lt; typename T & gt; class wellknown_iterator {
 T impl;
public:
 wellknown_iterator (T & amp; & amp; impl): impl (std :: move (impl)) {}
 wellknown_iterator () {}
 // ...
 T & amp; operator * () {return impl.current (); }
 T operator * () const {return impl.current (); }
}

In this case, the IContainer and Container classes would look something like this:

class IContainer {
 // ...
protected:
 virtual IIterator * create_iterator () = 0;
public:
 iterator begin () {return create_iterator (); }
 iterator end () {return nullptr; }
}
class Container: IContainer {
public:
 class Iterator: IIterator {
  // ...
 };
 // ...
protected:
 virtual IIterator * create_iterator () {return new Iterator (this); };
public:
 wellknown_iterator & lt; Iterator & gt; begin () {return Iterator (this); }
 wellknown_iterator & lt; Iterator & gt; end () {return wellknown_iterator & lt; Iterator & gt; (); }
}

Answer 2, authority 40%

The essence of iterators is that they are a “wrapper” around a pointer and are always used by value. When an object of a derived class returns from a function, it is converted to an object of the base class (IOuterIterator) and forgets who it was. Therefore, even after fixing errors, the architecture will not be valid – all iterators will work as basic, that is, in no way.

To avoid this, you should immediately mark the IOuterIterator methods as abstract (= 0) and warn yourself against incorrect actions.

And the error is due to the fact that there is no OuterIterator constructor from IOuterIterator.


My fix suggestion is to rename IOuterIterator to IOuterIteratorImpl, create additional class

template & lt; class T & gt;
class IOuterIterator {
  std :: unique_ptr & lt; IOuterIteratorImpl * & gt; _impl;
  / * corresponding code * /
public:
  T & amp; operator * () {return ** _ impl; };
  /* etc */
};

Then it can be used by value, leaving the previous functionality.

Programmers, Start Your Engines!

Why spend time searching for the correct question and then entering your answer when you can find it in a second? That's what CompuTicket is all about! Here you'll find thousands of questions and answers from hundreds of computer languages.

Recent questions