I am attempting to use polymorphism with a templated derived class. Consider the following code:
// base class
class A {
public:
virtual void f() = 0;
};
// templated derived class
template< typename T >
class B : public A {};
template <> //define a specialization of B
class B< int > {};
// trying to define a specialization of f
template <>
void B< int >::f() {}
My base class has the pure virtual function f. I am storing a vector of base class pointers A* and would like to call f on all of them, with appropriate polymorphism on the templated derived class. However, I am unable to define specializations of f, as I receive the following error:
test.cpp:17:18: error: no member function ‘f’ declared in ‘B<int>’ void B< int >::f() {}
Obviously the error here is that f is not actually a member function of the templated class. Is there any way to define specializations of f (or something nearly equivalent), or is this simply not possible? If it is not possible, can you suggest another approach?
[Apologies if this is a duplicate--I searched and found many variations of questions on templating, inheritance, and polymorphism, but none that exactly matched mine.]
template <> //define a specialization of B
class B< int > {};
Your specialization does not define the overriden virtual function. Change this specialization to:
template <> //define a specialization of B
class B< int > : public A
{
public:
void f() override;
}
A specialization is like defining a new class. You have to define everything that's in it. If the specialization should have a particular method: define it.
EDIT: also corrected the typo inherited from the original question.
Related
Compiling the following contrived example:
class Base
{};
class Derived : public Base
{};
template< typename T >
class A
{};
class B
{
public:
static void f( const A< Base >& ) {}
};
int main()
{
A< Base > tb;
A< Derived > td;
B::f( tb );
B::f( td );
return 0;
}
using g++-8 gives me the following error:
error: no matching function for call to 'B::f(A<Derived>&)'
B::f( td );
note: no known conversion for argument 1 from 'A<Derived>' to 'const A<Base>&'
Why?
Since Derived is-a Base and it doesn't override any of Base's stuff, why can't I give a Derived in the place of a Base in the templated function parameter?
It is true that Derived is derived from Base, but that doesn't mean that A<Derived> must therefore be derived from A<Base>. C++ templates don't work this way.
All that A<Derived> is, is a class, instantiated by the A template. You could've simply declared:
class A_Derived {
// ...
};
With the same members (if it had any), and pretty much got the same results. Same for A<Base>. With nothing else in the picture, the two classes have absolutely nothing to do with each other, whatsoever. You can draw a mental picture here, as if you made the following declarations:
class A_Derived {
};
and
class A_Base {
};
Which is pretty much what this is history. Do you see A_Derived being explicitly derived from A_Base here? Obviously not. If something expects a reference or a pointer to A_Base, you cannot give it A_Derived, because the two classes have absolutely nothing to do with each other. They are independent classes.
P.S. You could declare an explicit specialization of A<Derived> as being derived from A<Base>, if you so wish, but specialization is a completely different topic...
Template instances, like A<Base> and A<Derived>, are different types. In particular they do not have any inheritance relationship even if Base and Derived do.
There are quite a few ways you can make what you want work.
First, you could make A<Derived> explicitly derive from A<Base>, but that means adding a whole class definition.
template<>
class A<Derived> : public A<Base>
{};
Second, you can provide an implicit conversion from A<Derived> to A<Base> in the form of a constructor template. You can use std::enable_if and std::is_base_of to only allow A<T> where T is derived from Base, or directly std::is_same if you only want to consider this particular Derived type.
template<typename T>
class A
{
template<typename U, typename = std::enable_if_t<std::is_base_of_v<T, U>>>
A(A<U> const& other);
};
Third, you can provide an implicit conversion in the form of an operator template, in much the same way.
template<typename T>
class A
{
template<typename U, typename = std::enable_if_t<std::is_base_of_v<U, T>>>
operator U();
};
Fourth, you can make f a function template and restrict what types it takes.
template<typename T, typename = std::enable_if_t<std::is_base_of_v<Base, T>>>
static void f(A<T> const& a);
Consider the following example:
template <typename T>
class A {
private:
typedef typename T::C C;
};
template <typename T>
class B : public A<B<T>> {
public:
typedef T C;
};
int main() {
B<int> b;
}
Compiling it with GCC gives the following error:
test.cc:5:23: error: no type named 'C' in 'B<int>'
typedef typename T::C C;
~~~~~~~~~~~~^
test.cc:9:18: note: in instantiation of template class 'A<B<int> >' requested here
class B : public A<B<T>> {
^
test.cc:15:10: note: in instantiation of template class 'B<int>' requested here
B<int> b;
^
Why does compiler give an error if B::C is defined and how to fix it?
At this point,
class B : public A<B<T>> {
… class B is incomplete. Class A can't look inside it.
The C type definition inside B is accessible from that point inside B, and on. It's also available inside function bodies in B because you can regard a function definition inside the class definition as a shorthand for placing it after the class. But an incomplete class contains nothing as viewed from outside: all that outside code can do is form pointers and references and use the class as template argument.
template< class C >
using Ungood = typename C::Number;
struct S
{
void foo() { Number x; (void) x; } // OK
Ungood<S> uhuh; //! Nyet.
using Number = double;
};
auto main() -> int {}
You can fix your code by changing the design. The most obvious is to pass the type as a separate template argument. But depending on what you're trying to achieve it may be that the inheritance you currently have, isn't really needed or even useful.
You can't because you're in a chicken-egg paradox. The definition of the base requires knowledge of the definition of the derived, which needs the definition of the base to complete. You simply have to come up with an alternative. One example would be to use an external metafunction to communicate the needed type to whoever needs it. Hopefully that's not in any part of the definition of the base's members or you're probably screwed.
Other alternative is to pass T as a second parameter.
You can't do that because of this:
A class is considered defined after the closing brace of its class-specifier has been seen [...]
And a few exceptions, none of which are valid in your case.
In other terms, you must consider your derived class as not fully defined when you try to use it in your base class to access the type C.
Anyway, you can exploit the fact that your derived class is a template class and do this:
template <typename T>
class A;
template <template<typename> class D, typename T>
class A<D<T>> {
private:
using C = T;
};
Aa you can see, I've not given a definition for the primary template class, thus only the specialization for template classes can be used.
Not sure this is the OP's real case, but it's the case in the example in the question.
Say I have a class template in which some methods are type specific.
template <typename T>
class Shape
{
...
void Foo();
...
};
Now I would specialize the type-specific functions using, for example:
void Shape<bool>::Foo() {...};
void Shape<double>::Foo() {...};
Question:
What if Foo should have different parameters depending on the type? Is it possible to specialize a method for a specific type using parameters different from the ones in the class declaration? I suspect it's not allowed, so how should it be done?
How to prevent class instantiation for types for which Foo hasn't been defined?
When should virtual and pure virtual functions should be used in contrast to the siutation I have described? Is it possible to mix both templates and inheritance?
1a . What if Foo should have different parameters depending on the type? Is it possible to specialize a method for a specific type using parameters different from the ones in the class declaration?
No. Yes, As pointed out by Nathan Monteleone below, this is possible by fully specializing the class Shape.
1b . I suspect it's not allowed, so how should it be done?
Can you turn the function itself into a template independent of the class
class Shape {
//...
template <typename T>
void Foo();
//...
};
2 . How to prevent class instantiation for types for which Foo hasn't been defined?
In the class body:
template<typename U> //note the different typename
class Shape {
static_assert(std::is_same<U, bool>::value || std::is_same<U, double>::value, "Type forbidden");
//...
inline void Foo() {
if( std::is_same<U, bool>::value ) Foo_impl_bool();
else if( std::is_same<U, double>::value ) Foo_impl_double();// and so on.
// this function is likely to get optimized since the conditionals are constexpr
}
private:
void Foo_impl_bool();//...
};
3a . When should virtual and pure virtual functions should be used in contrast to the situation I have described?
The answers to 1b and 2 are 2 separate solutions, if neither of them is possible the other possibilities are:
Extract that function into its own class.
Use interface inheritance for this function alone.
3b . Is it possible to mix both templates and inheritance?
Yes
(1) Sort of, but not quite the way you're trying to do it. You basically want the specializations to define the function; it's quite a bit different from when you are declaring base and derived classes.
template <typename T> class Shape {
static_assert(false, "Not implemented");
};
template <> class Shape<bool> {
void Foo(bool a, std::string b) {...}
};
template <> class Shape<int> {
void Foo(int, std::string, whatever else) {...}
};
Even if you were to define Foo in the original non-specialized Shape, it would have no effect on the specializations. Full specializations don't extend the original template, they replace it!
(2) Use static_assert. See the example above.
(3) Yes, you can mix templates and inheritence. Virtual functions runtime polymorphism and static polymorphism. Template parameters have to be known at compile time whereas the exact type of a runtime polymorphic object does not, because they can be referred to by a base class reference. In the templated example above you cannot simply say
template <class T> Shape {...};
...
Shape* myShape; // Compiler error: Shape requires a template parameter
However with inheritence you can:
class ShapeBase { virtual void Foo() = 0; };
class Circle : public ShapeBase { virtual void Foo() { ... } };
class Square : public ShapeBase { virtual void Foo() { ... } };
...
Shape* myShape = new Circle;
myShape->Foo(); // calls Circle->Foo()
Note when you're working with inheritence, the function signatures DO have to match! That's the price you pay for deferring the decision till runtime.
I have a template function which is explicitly instantiated for Base class, but not for Derived class. How can I force the uses that pass a Derived class (or other derived classes) to match against the Base class?
Header file:
class Base {
};
class Derived : public Base {
};
class Derived2 : public Base {
};
template <typename Example> void function(Example &arg);
Implementation file:
// Explicitly instantiate Base class:
template void function<Base>(Base &arg);
// Define the template function:
template <typename Example> void function(Example &arg) {
// Do something.
}
Because I have not explicitly instantiated function for Derived or Derived2, I get undefined references, however, I would like to bind against the Base class which is explicitly defined.
How can I force the template to resolve to the Base class for all objects derived from Base using C++-03?
Can I do it somehow with a specialization of the Derived class to the Base class definition?
How about:
template <> void function(Derived &arg)
{
function<Base>( arg );
}
EDIT: You can also do this with function overloading, as aschepler has suggested:
void function(Derived &arg)
{
function<Base>( arg );
}
It's conceptually the same, although, I agree, slightly better :)
In the CRTP pattern, we run into problems if we want to keep the implementation function in the derived class as protected. We must either declare the base class as a friend of the derived class or use something like this (I have not tried the method on the linked article). Is there some other (simple) way that allows keeping the implementation function in the derived class as protected?
Edit: Here is a simple code example:
template<class D>
class C {
public:
void base_foo()
{
static_cast<D*>(this)->foo();
}
};
class D: public C<D> {
protected: //ERROR!
void foo() {
}
};
int main() {
D d;
d.base_foo();
return 0;
}
The above code gives error: ‘void D::foo()’ is protected with g++ 4.5.1 but compiles if protected is replaced by public.
It's not a problem at all and is solved with one line in derived class:
friend class Base< Derived >;
#include <iostream>
template< typename PDerived >
class TBase
{
public:
void Foo( void )
{
static_cast< PDerived* > ( this )->Bar();
}
};
class TDerived : public TBase< TDerived >
{
friend class TBase< TDerived > ;
protected:
void Bar( void )
{
std::cout << "in Bar" << std::endl;
}
};
int main( void )
{
TDerived lD;
lD.Foo();
return ( 0 );
}
As lapk recommended, problem can be solved with simple friend class declaration:
class D: public C<D> {
friend class C<D>; // friend class declaration
protected:
void foo() {
}
};
However, that exposes all protected/private members of derived class and requires custom code for each derived class declaration.
The following solution is based on the linked article:
template<class D>
class C {
public:
void base_foo() { Accessor::base_foo(derived()); }
int base_bar() { return Accessor::base_bar(derived()); }
private:
D& derived() { return *(D*)this; }
// accessor functions for protected functions in derived class
struct Accessor : D
{
static void base_foo(D& derived) {
void (D::*fn)() = &Accessor::foo;
(derived.*fn)();
}
static int base_bar(D& derived) {
int (D::*fn)() = &Accessor::bar;
return (derived.*fn)();
}
};
};
class D : public C<D> {
protected: // Success!
void foo() {}
int bar() { return 42; }
};
int main(int argc, char *argv[])
{
D d;
d.base_foo();
int n = d.base_bar();
return 0;
}
PS: If you don't trust your compiler to optimize away the references, you can replace the derived() function with the following #define (resulted in 20% fewer lines of disassembly code using MSVC 2013):
int base_bar() { return Accessor::base_bar(_instance_ref); }
private:
#define _instance_ref *static_cast<D*>(this) //D& derived() { return *(D*)this; }
After some I came with a solution that works event for private members of templated derived classes. It does not solves the problem of not exposing all the members of the derived class to the base, since it uses a friend declaration on the whole class. On the other hand, for the simple case, this does not requires repeating the base name, nor it's template parameters and will always work.
First the simple case when the derived is non-template. The base takes an additional void template parameter just to show that everything still works in the case of extra template parameters of the base. The only needed one, as per the CRTP, is the typename Derived.
//Templated variadic base
template <typename Derived, typename...>
struct Interface
{
using CRTP = Interface; //Magic!
void f() { static_cast<Derived*>(this)->f(); }
};
//Simple usage of the base with extra types
//This can only be used when the derived is NON templated
class A : public Interface<A, void>
{
friend CRTP;
void f() {}
};
The only thing needed for this to work is the using CRTP = Interface; declaration in the base and the friend CRTP; declaration in the derived.
For the case when the derived is itself templated the situation is trickier. It took me some time to come to the solution, and I'm sure it's still not perfect.
Most of the magic happens inside these templates:
namespace CRTP
{
template <template <typename, typename...> class _Base, typename _Derived, typename... _BaseArgs>
struct Friend { using Base = _Base<_Derived, _BaseArgs...>; };
template <template <typename, typename...> class _Base, typename ..._BaseArgs>
struct Base
{
template <template <typename...> class _Derived, typename... _DerivedArgs>
struct Derived : public _Base<_Derived<_DerivedArgs...>, _BaseArgs...> {};
};
}
Their usage is more or less straightforward. Two use the above templates several steps are needed.
First, when inheriting in the derived class the inherited-from base class, and it's optional parameters, needs to be given. This is done using CRTP::Base<MyBase, BaseOptional....>, where MyBase is the name of the class used for CRTP, and the BaseOptional... are template parameters that are passed to the base class as-is, directly after passing our derived class that is supplied in the next step. When the base class does not accepts any additional template parameters they can be omitted completely: CRTP::Base<MyBase>.
The next step is to introduce the derived class (the whole point of CRTP). This is done by following the above CRTP::Base<...> with a ::Derived<ThisDerived, DerivedOptional...>. Where ThisDerived is the class this is defined in, and DerivedOptional... are all the template parameters declared in this class'es template declaration. The optional parameters much be specified exactly as they appear in the class template declaration.
The last step is declaring the base class as a friend. This is done by declaring friend typename CRTP::Friend<MyBase, ThisDerived, BaseOptional...>::Base somewhere in the class. The BaseOptional... template perameters must be repeated exactly as they appear in the CRTP::Base<MyBase, BaseOptional...> that is inherited from.
Follows is an example of using a templated derived when the base does not depends on the templated types (but it still can take other template parameters, void in this example).
//Templated derived with extra, non-dependant types, passed to the base
//The arguments passed to CRTP::Base::Derived<, ARGS> must exactly match
// the template
template <typename T, typename... Args>
class B : public CRTP::Base<Interface, void>::Derived<B, T, Args...>
{
friend typename CRTP::Friend<Interface, B, void>::Base;
void f() {}
};
Next is an example for when the base depends on template parameters of the derived. The only difference from the previous example is the template keyword. An experiment shows that if the keyword is specified for the previous, non dependant, case the code also complies cleanly.
//Templated derived with extra dependant types passed to the base
//Notice the addition of the "template" keyword
template <typename... Args>
class C : public CRTP::Base<Interface, Args...>::template Derived<C, Args...>
{
friend typename CRTP::Friend<Interface, C, Args...>::Base;
void f() {}
};
Please note that these templates do not work for non-templated derived classes. I will update this answer when I find the solution, so a unified syntax could be used for all cases. The closest thing that can be done is just using some fake template parameter. Note that it still must be named and passed to the CRTP machinery. For example:
template <typename Fake = void>
class D : public CRTP::Base<Interface>::Derived<D, Fake>
{
friend typename CRTP::Friend<Interface, D>::Base;
void f() {}
};
Note that A, B, C & D are declared as class. That is, all their members are private.
Follows is some code that uses the above classes.
template <typename... Args>
void invoke(Interface<Args...> & base)
{
base.f();
}
int main(int, char *[])
{
{
A derived;
//Direct invocation through cast to base (derived.f() is private)
static_cast<A::CRTP &>(derived).f();
//Invocation through template function accepting the base
invoke(derived);
}
{
B<int> derived;
static_cast<B<int>::CRTP &>(derived).f();
invoke(derived);
}
{
C<void> derived;
static_cast<C<void>::CRTP &>(derived).f();
invoke(derived);
}
{
D<void> derived;
static_cast<D<>::CRTP &>(derived).f();
invoke(derived);
}
return 0;
}
The invoke free-standing templated function works for any class derived from the base.
Also shown is how to cast the derived to the base without the need to actually specify the name of the base.
Surprisingly, this does not depend on any system headers.
The full code is available here: https://gist.github.com/equilibr/b27524468a0519aad37abc060cb8bc2b
Comments and corrections are welcome.