I have the following Header/Source files:
// foo.h
#ifndef __FOO_H
#define __FOO_H
#include <map>
#include <stdexcept>
template <typename T>
class FooBase {
public:
std::map<float,T> a;
FooBase(std::map<float,T> a_) : a(a_) {};
T func(float x1, T fallback) const;
virtual T func(float x1) const = 0;
};
class Foo: public FooBase<float> {
public:
Foo() : FooBase<float>({}) {};
float func(float x1) const;
// float func(float x1, float fallback) const;
};
void test_foo1();
void test_foo2();
#endif // __FOO_H
// foo.cpp
#include "foo.h"
template <typename T>
T FooBase<T>::func(float x1, T fallback) const {
try {
return (func(x1));
} catch(std::runtime_error&) {
return fallback;
}
};
float Foo::func(float x1) const {
return 2.0;
};
template class FooBase<float>;
void test_foo1() {
Foo foo;
float b = foo.func(1.0, 2.0);
}
void test_foo2() {
Foo foo;
float b = foo.func(1.0);
}
It is crucial, that the "fallback"-Version of func
has the same name as a virtual function but different signature, whereas the single-signature version of func
will be specified in the child class Foo
.
The test_foo1
-Function won't compile:
[ 3%] Building CXX object foo.cpp.o
foo.cpp: In function ‘void test_foo1()’:
foo.cpp:20:29: error: no matching function for call to ‘Foo::func(double, double)’
20 | float b = foo.func(1.0, 2.0);
| ^
foo.cpp:12:7: note: candidate: ‘virtual float Foo::func(float) const’
12 | float Foo::func(float x1) const {
| ^~~
foo.cpp:12:7: note: candidate expects 1 argument, 2 provided
If I activate the commented declaration line in foo.h
, it compiles, but won't be able to resolve when test_foo1
is called in another source file. In this case I used a catch2
-Test which fails when test_foo1
is included, while test_foo2
makes no problems:
[ 98%] Linking CXX executable ...
/usr/bin/ld: libFoo.so: undefined reference to `Foo::func(float, float) const'
Can anyone tell me if I'm totally wrong in how to achieve this? I hesitate to share the full logic of the build, because possibly the repeated declaration might be wrong approach anyways. I hope that fact that test_foo2
works in the other source file generates enough trust to rule out problems located outside of the shared code.
The basic idea is in the GOF-language a "template-method pattern" with a hook-function that has no default. Special is that the hook-method has the same name as the function calling it. The fact that the class has state (a
) and a template parameter might make an effect, at least I think that I achieved something like this in simpler situations, therefore I included the state and the template parameter in the "minimal example", hope that does not make things too complicated.
Thanks for any help.
Foo::func
hides FooBase::func
. You can use using
to pull FooBase::func
into Foo
s scope:
class Foo: public FooBase<float> {
public:
Foo() : FooBase<float>({}) {};
float func(float x1) const;
using FooBase<float>::func;
};
Alternatively you can explicitly pick FooBase::func
to be called:
void test_foo1() {
Foo foo;
float b = foo.FooBase<float>::func(1.0, 2.0);
}