Template specialization of pure virtual function
//virtual void func(int a) {}
// replace above line with this and it works
Replace the above line and the code compile, doesn't works.
Or, better, works but not as you expect.
The problem is that virtual
functions and template
functions doesn't mix very well.
So you can't make a template function that directly override a virtual function: if you define func()
as a null virtual function
virtual void func(int a) = 0;
the base A
class, and all derived classes, become not-instantiable until you don't define an effective virtual
func()
function.
Defining
virtual void func(int a) {}
the base A
class, and all derivate, isn't not-instantiable anymore a you don't need anymore a redefinition of the virtual function.
But the template
func()
version is not related with virtual function.
When you call b.func(2)
in main()
, it's the template
, not the virtual
func()
inherited from A
, that is called. It's because the template
func()
"hide" the func()
inherited virtual
version.
You can "un-hide" the virtual
func()
version in B
adding, in the body of B
definition
using A::func;
This way, calling b.func(2);
in main()
, the virtual
version inherited by A
is called and the template specialization of func()
, so the std::cout <<"hello 2" << std::endl;
instruction, ins't executed anymore.
Now... if I understand correctly, you want a template
func()
function that, in case of T == int
join the virtual specialization.
The only way I see is define the virtual
override
in B
void func (int a) override // override, so necessarily virtual
{ std::cout <<"hello 2" << std::endl; }
and call it from the template
specialization
template <>
void B::func<int> (int a)
{ func(a); } // call the virtual override version
The following is a full compiling example
#include <iostream>
struct A
{ virtual void func(int a) = 0; };
struct B : public A
{
void func (int a) override
{ std::cout <<"hello 2" << std::endl; }
template<typename T>
void func (T t)
{ std::cout << "hello" << std::endl; }
};
template <>
void B::func<int> (int a)
{ func(a); }
int main ()
{
B{}.func(2); // call directly virtual func()
B{}.func<int>(2); // call template func() specialization that call virtual func()
}
C++ Override Template class virtual function in Template class
A compiling version of your code:
template <typename data_t, short value_v>
class Base
{
public:
Base() = default; // <== was missing impl, set to default
virtual ~Base() = default; // classes with virtual methods must have virtual destructor
virtual void clear() = 0;
protected:
int variable1;
};
template <typename data_t = short, short value_v = 100>
class Derived :
public Base<data_t, value_v>
{
// by adding a using you can more easily change the template params
// without having to update all your code. Helps keeping code more readable too
using base_t = Base<data_t, value_v>;
public:
void clear() override // <== missing override
{
// different ways of accessing member in template base class
int v1 = Base<data_t, value_v>::variable1;
int v2 = this->variable1;
int v3 = base_t::variable1;
}
};
int main()
{
Derived<short,100> d;
d.clear();
}
Virtual functions or SFINAE for template specialisation... or a better way?
I'm still very open to answers and feedback, however, I at least found a less verbose and more elegant solution to what I had before, by is using if constexpr
(C++17 is pretty awesome!):
using col = Eigen::Block< fvm::field<n> >;
using constCol = const Eigen::Block< const fvm::field<n> >;
using elem = typename std::conditional<n==1, fvm::scalar&, col >::type;
using constElem = typename std::conditional<n==1, fvm::scalar , constCol>::type;
elem operator[] (int) {
if constexpr ( n==1 )
return m_data[i];
else
return m_data.block(0, i, n, 1);
}
constElem operator[] (int) const {
if constexpr ( n==1 )
return m_data[i];
else
return m_data.block(0, i, n, 1);
}
EDIT: What makes this solution even better, is that now, automatic type deduction actually works, reducing the code to
auto operator[] (int) {
if constexpr ( n==1 )
return m_data[i];
else
return m_data.block(0, i, n, 1);
}
auto operator[] (int) const {
if constexpr ( n==1 )
return m_data[i];
else
return m_data.block(0, i, n, 1);
}
EDIT2: I was wrong about auto
being usable. The .obj-files of my unit test executable simply still had the correct type stored, but they couldn't be recreated using auto
.
C++ Virtual template method
The problem is that you cannot mix static time polymorphism (templates) with runtime polymorphism easily. The reason for the language disallowing the particular construct in your example is that there are potentially infinite different types that could be instantiating your template member function, and that in turn means that the compiler would have to generate code to dynamically dispatch those many types, which is infeasible.
There are different things that can be done here to get around the limitation, basically either take away the static or the dynamic polymorphism. Removing dynamic polymorphism from the equation could be done by providing a type that is not derived from, to store the <key,value>
mappings, and then offering the template that resolves that only at the base level:
class AbstractComputation {
public:
template <typename T>
void setData( std::string const & id, T value ) {
m_store.setData( id, value );
}
template <typename T>
T getData( std::string const & id ) const {
return m_store.getData<T>( id );
}
protected:
ValueStore m_store;
};
Now deriving classes can access the ValueStore
from the base and there is no need for polymorphism. (This can also be done by implementing the functionality directly in AbstractComputation
but it probably makes sense to separate concerns)
The other option is to maintain runtime polymorphism, but remove static polymorphism. This can be done by performing type erasure on the base class and then dispatching to the appropriate (non-templated) function that takes the type-erased arguments. The simplest version of this is just using boost::any
:
class AbstractComputation {
public:
template <typename T>
void setData( std::string const & id, T value ) {
setDataImpl( id, boost::any( value ) );
}
template <typename T>
T getData( std::string const & id ) const {
boost::any res = getDataImpl( id );
return boost::any_cast<T>( res );
}
protected:
virtual void setDataImpl( std::string const & id, boost::any const & value ) = 0;
virtual boost::any getDataImpl( std::string const & id ) const = 0;
};
How type erasure is implemented under the hood is interesting, but out of the scope here, the important part is that a boost::any
is a concrete (non-templated) type that can store any type internally by using type erasure on the arguments, and at the same time allows for type-safe retrieval of the data.
Related Topics
Extern "C" Linkage Inside C++ Namespace
Visual Studio Compiler Warning C4250 ('Class1':Inherits 'Class2::Member' via Dominance)
How to Read Input When Debugging in C++ in Visual Studio Code
Adding Quotes to Argument in C++ Preprocessor
What's the Difference Between C and C++
Why Double Can Store Bigger Numbers Than Unsigned Long Long
How to Implement a Video Widget in Qt That Builds Upon Gstreamer
How to Get a List Video Capture Devices Names (Web Cameras) Using Qt (Crossplatform)? (C++)
Floating Point to Binary Value(C++)
Cmake: How to Change Properties on Subdirectory Project Targets
Does the Alignas Specifier Work with 'New'
How to Create a Temporary Text File in C++
How to Wrap Functions with the '--Wrap' Option Correctly
How to Check for Inf (And | Or) Nan in a Double Variable
Cannot Evaluate Function -- May Be Inlined
For-Loop in C++ Using Double Breaking Out One Step Early, Boundary Value Not Reached