Home > Net >  virtual method ignored in tempate inheritance
virtual method ignored in tempate inheritance

Time:06-10

I've tried searching for some explanations about how this exact pattern of inheritance works, but never found anything quite similar, so I hope some of you guys know what's going on.

So here's the behaviour I want to get:

#include <iostream>

template <typename T, typename F = std::less<T>>
class Base 
{
protected:
    F obj;
public:
    virtual bool f(T t1, T t2) { return obj(t1, t2); }
};

template <typename T>
struct Derived : public Base<T, std::less<T>>
{
public:
    bool f(T t1, T t2) { return this->obj(t2, t1); }
};

int main()
{
    Base<int> b;
    std::cout << std::boolalpha << b.f(1, 2) << '\n';
    Derived<float> d;
    std::cout << std::boolalpha << d.f(1, 2) << '\n';
}

Here I redefine f() in Derived and change how the class behaves. The program prints out

true
false

just like it should.

However, when I try to modify it so that Derived could take a pair but only compare the .first fields like this

#include <iostream>

template <typename T, typename F = std::less<T>>
class Base 
{
protected:
    F obj;
public:
    virtual bool f(T t1, T t2) { return obj(t1, t2); }
};

template <typename T>
struct Derived : public Base<std::pair<T, T>, std::less<T>>
{
public:
    typedef std::pair<T, T> pair;
    bool f(pair t1, pair t2) { return this->obj(t1.first, t2.first); }
};

int main()
{
    Base<int> b;
    std::cout << std::boolalpha << b.f(1, 2) << '\n';
    Derived<int> d;
    std::cout << std::boolalpha << d.f(std::make_pair(1, 2), std::make_pair(2, 2)) << '\n';
}

the compiler goes all agro on me:

file.cpp:9:41: error: no match for call to ‘(std::less<int>) (std::pair<int, int>&, std::pair<int, int>&)’
    9 |  virtual bool f(T t1, T t2) { return obj(t1, t2); }
      |                                      ~~~^~~~~~~~
...
/usr/include/c  /10/bits/stl_function.h:385:7: note: candidate: ‘constexpr bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = int]’   
  385 |       operator()(const _Tp& __x, const _Tp& __y) const
      |       ^~~~~~~~
/usr/include/c  /10/bits/stl_function.h:385:29: note:   no known conversion for argument 1 from ‘std::pair<int, int>’ to ‘const int&’
  385 |       operator()(const _Tp& __x, const _Tp& __y) const
      |                  ~~~~~~~~~~~^~~

So it's clear that the Base::f() is called instead of the redefinition. But why is this happening when in the first example everything was fine and the virtual functions behaved as intended?

CodePudding user response:

virtual means that either Base::f or Derived::f is called depending on the dynamic type of the object.

The fact that you only call Derived<int>::f in main does not change that Base<std::pair<int,int>,std::less<int>::f is not valid.

A much simpler example with same issue is this:

#include <utility>


struct base {
    virtual std::pair<int,int> foo(std::pair<int,int> x) { 
        return x x;
    }
};

struct derived : base {
    std::pair<int,int> foo(std::pair<int,int> x) override {
        return x;
    }
};

int main() {
    derived d;
}

Resulting in error:

<source>: In member function 'virtual std::pair<int, int> base::foo(std::pair<int, int>)':
<source>:6:17: error: no match for 'operator ' (operand types are 'std::pair<int, int>' and 'std::pair<int, int>')
    6 |         return x x;
      |                ~^~
      |                | |
      |                | pair<[...],[...]>
      |                pair<[...],[...]>
Compiler returned: 1

Depending on what you are actually trying to achieve you might want to make Base::f pure virtual, or provide a specialization of Base when T is a std::pair, or inherit from Base<std::pair<T,T>,std::less<std::pair<T,T>>>.

  • Related