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>>>
.