Template specialization and inheritance - c++

Suppose I have a template class with a lot of functions and I want to specialize them to change only a few of them and keep the other ones exactly as specified in the base template class.
How can I do that?
Below is what I want to achieve, but the solution isn't good, because it does not allow me to refer to the specialization for int as Base<int> – I need to use IntSpec for that.
#include <iostream>
using namespace std;
template<typename T>
struct Base
{
void print1() {cout << "Base::print1" << endl;};
void print2() {cout << "Base::print2" << endl;};
};
struct IntSpec : public Base<int>
{
void print2() {cout << "Base<int>::print2()" << endl;};
};
int main()
{
Base<double> d;
// Base<int> i; <-- I want this kind of instantiation
IntSpec i;
d.print1();
d.print2();
i.print1();
i.print2();
}
The output is:
Base::print1
Base::print2
Base::print1
Base<int>::print2()

Nicol's solution works fine, but this is an alternative:
template<typename T>
struct Base
{
void print1() {cout << "Base::print1" << endl;};
void print2() {cout << "Base::print2" << endl;};
};
template<>
void Base<int>::print2() {cout << "Base<int>::print2()" << endl;};
That way you can specialize only specific member functions and still use those that you haven't specialized(in this case, print1) without any problem. So now you'd use it just like you wanted:
Base<int> i;
i.print1();
i.print2(); // calls your specialization
Demo here.

You just have to use two template classes:
template<typename T>
struct CommonBase
{
void print1() {cout << "Base::print1" << endl;};
void print2() {cout << "Base::print2" << endl;};
};
template<typename T>
struct Base : public CommonBase<T>
{
};
template<>
struct Base<int> : public CommonBase<int>
{
void print2() {cout << "Base::print2" << endl;};
};
You always use Base, rather than CommonBase.

Another solution would be to add a level of indirection in the function you want to redefine, i.e.
template<typename T>
struct foo
{
template<typename T2>
void bar_impl()
{
//generic function
}
void bar()
{
bar_impl<T>();
}
};
Then you can specialize each function individually for each type or specialize the whole type as wanted.

Related

How to call function in primary template for a specialization of the class template?

If I have a normal function in a class template, when I specialize the class template, could I call the normal function as well?
Such as I have a template class
template<int i>
class A
{
public:
void func() {
std::cout << "A" << std::endl;
}
};
And its specialization
template<>
class A<1>
{
public:
void func1() {
cout << "A1" << endl;
}
};
Now in main
int main()
{
auto a1 = A<1>();
a1.func1();
a1.func(); //<--- here
}
How could I call a1.func()?
The class A<1> doesn't have a member func. The primary template is ignored when you specialise. If you want it to behave like inheritance, you could use inheritance.
class BaseA
{
public:
void func() {
std::cout << "A" << std::endl;
}
};
template<int>
class A : public BaseA {};
template<>
class A<1> : public BaseA
{
public:
void func1() {
cout << "A1" << endl;
}
};
Now A<1> inherits func from BaseA, and A<2> doesn't have func1, but does have func.

using intermediate class in a Crtp hierarchy without declaring a new class

I have a hierarchy similar to the following:
#include <iostream>
template<typename DerivedCrtp>
struct A
{
void Print() { std::cout << "A";}
};
struct B : public A<B>
{
};
template<typename DerivedCrtp>
struct C : public A<C<DerivedCrtp>>
{
void Print() { std::cout << "C";}
};
template<typename DerivedCrtp>
struct D : public C<D<DerivedCrtp>>
{
void Print() { std::cout << "D";}
};
struct CFinalized : public C<CFinalized>
{
void Print() { std::cout << "CFinal";}
};
template<typename DerivedCrtp = CSmart<>>
struct CSmart : public A<C<DerivedCrtp>>
{
void Print() { std::cout << "C";}
};
int main()
{
C<int> c;
D<int> d;
CFinalized cf;
c.Print();
d.Print();
cf.Print();
}
Because C is using crtp I can't directly use it without providing the self derived type DerivedCrtp.
In order to use it I need to "finalize" it's type (see CFinalized).
It works but every time I need to use a class part of that hierarchy (that in my real code is deeper and contains several more template parameters), I need to explicitly declare a new class.
Is there a smarter way to do this?

Default fallback for C++ template functions using enable_if

I want to write a C++ mechanism, where different instantiations of a function are called if a given class Param is derived from a certain base class.
This works pretty nicely with std::is_base_of and std::enable_if.
However, I would like to have a "default version" of this doStuff() function that is called for "every other class".
This would basically work by doing something like "if Param is not derived from A and if not derived from B", but I wonder whether there is a more elegant solution.
#include <iostream>
class A {};
class B : public A {};
class X {};
class Y : public X {};
class Other {};
template <typename Param, std::enable_if_t<std::is_base_of<A, Param>::value, bool> = true>
void doStuff() {std::cout << "Base A" << std::endl;};
template <typename Param, std::enable_if_t<std::is_base_of<X, Param>::value, bool> = true>
void doStuff() {std::cout << "Base X" << std::endl;};
int main()
{
doStuff<B>();
doStuff<Y>();
// doStuff<Other>(); this is neither derived from B and Y, so call the default case
}
The solution should work with C++14.
When using std:::enable_if, you will have to provide a 3rd SFINAE'd overload that handles the default conditions which are not handled by the other overloads, eg:
#include <iostream>
#include <type_traits>
class A {};
class B : public A {};
class X {};
class Y : public X {};
class Other {};
template <typename Param, std::enable_if_t<std::is_base_of<A, Param>::value, bool> = true>
void doStuff() { std::cout << "Base A" << std::endl; }
template <typename Param, std::enable_if_t<std::is_base_of<X, Param>::value, bool> = true>
void doStuff() { std::cout << "Base X" << std::endl; }
template <typename Param, std::enable_if_t<!(std::is_base_of<A, Param>::value || std::is_base_of<X, Param>::value), bool> = true>
void doStuff() { std::cout << "Something else" << std::endl; }
int main()
{
doStuff<B>(); // prints "Base A"
doStuff<Y>(); // prints "Base X"
doStuff<Other>(); // prints "Something else"
}
Online Demo
That being said, in C++17 and later, you can use if constexpr instead, which is cleaner than using SFINAE in this situation, eg:
#include <iostream>
#include <type_traits>
class A {};
class B : public A {};
class X {};
class Y : public X {};
class Other {};
template <typename Param>
void doStuff() {
if constexpr (std::is_base_of_v<A, Param>)
std::cout << "Base A" << std::endl;
else if constexpr (std::is_base_of_v<X, Param>)
std::cout << "Base X" << std::endl;
else
std::cout << "Something else" << std::endl;
}
int main()
{
doStuff<B>(); // prints "Base A"
doStuff<Y>(); // prints "Base X"
doStuff<Other>(); // prints "Something else"
}
Online Demo
Or if you can use C++17 or later use constexpr, a lot more readable then SFINAE
#include <type_traits>
#include <iostream>
class A {};
class B : public A {};
class X {};
class Y : public X {};
class Other {};
template<typename type_t>
void doStuff()
{
if constexpr (std::is_base_of_v<A,type_t>)
{
std::cout << "Base A\n";
}
else
if constexpr (std::is_base_of_v<X, type_t>)
{
std::cout << "Base X\n";
}
else
{
std::cout << "Other\n";
}
};
int main()
{
doStuff<B>();
doStuff<Y>();
doStuff<Other>(); //this is neither derived from B and Y, so call the default case
return 0;
}

How to declare a template method of a template base class using 'using'?

Two methods for accessing template base class members are described here. When the base class member itself is a template, accessing it using the first method (this->) above is described here. Is there a way to use the second method (using xxxx) in this scenario as well?
For example, in the code below, is it possible to replace "????" with something to make the code work?
using namespace std;
template<typename T> class base
{
public:
template<bool good> void foo()
{
cout << "base::foo<" << boolalpha << good << ">()" << endl;
}
};
template<typename T> class derived : public base<T>
{
public:
using ????
void bar()
{
foo<true>();
}
};
Just for the record, even though it does not provide any answer or workaround, but I am currently working with VS Express 2013, and I can assure you that
#include <iostream>
template<typename T> class base
{
public:
template<bool good> void foo()
{
std::cout << "base::foo<" << good << ">()" << std::endl;
}
};
template<typename T> class derived : public base<T>
{
public:
void bar()
{
foo<true>();
}
};
struct A{};
void main() {
derived<A> a;
a.bar();
}
works perfectly fine...

Template specialization of inherited member function

We would like to specialize member functions of a base class. However, it does not compile. Does anybody know of any alternative that does compile?
Here is an example
struct Base
{
template<typename T>
void Foo()
{
throw "Foo() is not defined for this type";
}
};
struct Derived : public Base
{
template<>
void Foo<int>() { cout << "Foo<int>()" << endl; } // compile error (cannot specialize members from a base class)
template<>
void Foo<double>() { cout << "Foo<double>()" << endl; } // compile error (cannot specialize members from a base class)
};
Eventually, we solved it using overloading.
Here is how the base class looks like
struct Base
{
template<typename T>
class OfType {}
template<typename T>
void Foo(OfType<T>) { static_assert(false, "Foo is not implemented for this type. Please look in the compiler error for more details."); }
};
struct Derived : public Base
{
using Base::Foo;
void Foo(OfType<int>) { // here comes logic for ints }
void Foo(OfType<double>) { // here comes logic for doubles }
};
Here is an example of client code that uses Foo()
template<typename S>
class ClassThatUsesFoo
{
private: S s;
template<typename T>
void Bar(T item)
{
s.Foo(Base::OfType<T>()); // this is the code that uses Foo
DoSomeStuffWithItem(item);
}
};
void main()
{
ClassThatUsesFoo<Derived> baz;
baz.Bar(12); // this will internally use Foo for ints
baz.Bar(12.0); // this will use Foo for doubles
baz.Bar("hello world"); // this will give a verbose compile error
}
This will compile, except for the call to Foo<char>():
#include <iostream>
#include <string>
using namespace std;
struct Base
{
template<typename T>
void Foo()
{
throw "Foo() is not defined for this type";
}
};
struct Derived : public Base
{
template<typename T> void Foo();
};
template<>
void Derived::Foo<int>() { cout << "Foo<int>()" << endl; }
template<>
void Derived::Foo<double>() { cout << "Foo<double>()" << endl; }
int main()
{
Derived derived;
// this is client code
derived.Foo<int>();
derived.Foo<double>();
derived.Foo<char>(); // this throws
}
If you want the call to Foo<char>() -- or any type not specifically specialized by you -- then this works. If you want a non-specialized implementation that works for all types, then you need to add a non-specialized implementation of Foo() as well:
template<typename T>
void Derived::Foo() { cout << "generic" << endl; }
In response to the discussion with Alex (see comments of the answer of John Dibling), this is what I meant (SSCCE):
#include <iostream>
using namespace std;
struct Base
{
template<typename T>
void Foo()
{
//static_assert(false, "Foo() is not defined for this type");
throw "Foo() is not defined for this type";
}
};
// you can add as many specializations in Base as you like
template <>
void Base::Foo<char>() { cout << "Base::Foo<char>()" << endl; }
struct Derived : public Base
{
// just provide a default implementation of Derived::Foo
// that redirects the call to the hidden Base::Foo
template < typename T >
void Foo()
{ Base::Foo<T>(); }
};
// the specializations for Derived
template<>
void Derived::Foo<int>() { cout << "Foo<int>()" << endl; }
template<>
void Derived::Foo<double>() { cout << "Foo<double>()" << endl; }
struct Derived_wo_specialization : public Base
{
/* nothing */
};
int main()
{
Derived d;
d.Foo<char>();
d.Foo<double>();
Derived_wo_specialization dws;
dws.Foo<char>();
dws.Foo<double>();
}