Overloading c++ template class method - c++

Can I overload a template class function in a class that extends its specialization?
I have the following piece of code (I've tried to simplify it to the bare minimum):
#include <iostream>
using namespace std;
class X {
public:
unsigned test_x() {
return 1;
}
};
class Y {
public:
unsigned test_y() {
return 2;
}
};
template <typename T, typename U>
class A {
public:
unsigned foo(U i) {
cout << "A" << endl;
return i.test_x();
}
unsigned bar(T i) {
return foo(i);
}
};
class B : public A<Y, X> {
public:
unsigned foo(Y i) {
cout << "B" << endl;
return i.test_y();
}
};
int main() {
B b = B();
Y y = Y();
cout << "Hello: " << b.bar(y) << endl;
return 0;
}
However the compiler produces the following error:
hello.cc: In member function ‘unsigned int A<T, U>::bar(T) [with T = Y, U = X]’:
hello.cc:47: instantiated from here
hello.cc:30: error: no matching function for call to ‘A<Y, X>::foo(Y&)’
hello.cc:24: note: candidates are: unsigned int A<T, U>::foo(U) [with T = Y, U = X]
Basically I would like to overload the function A::foo() in its derived class B.

Apparently what you're asking is called "static polymorphism" and it's achieved by the means of "curiously recurring template pattern":
template <typename Derived, typename T, typename U>
class A {
public:
unsigned foo(U i) {
cout << "A" << endl;
return i.test_x();
}
unsigned bar(T i) {
return static_cast<Derived*>(this)->foo(i);
}
};
class B : public A<B, Y, X> {
public:
// Uncomment this line if you really want to overload foo
// instead of overriding. It's optional in this specific case.
//using A::foo;
unsigned foo(Y i) {
cout << "B" << endl;
return i.test_y();
}
};

I don't think the overloads in derived classes cannot be used from the base class where they are to be called from.
What you can do, is to extract the foo method, so you can specialize it for B.
#include <iostream>
using namespace std;
class X {
public:
unsigned test_x() {
return 1;
}
};
class Y {
public:
unsigned test_y() {
return 2;
}
};
template <typename T, typename U>
struct Foo
{
unsigned foo(U i) {
cout << "A" << endl;
return i.test_x();
}
};
template <typename T, typename U>
class A : public Foo<T, U> {
public:
unsigned bar(T i) {
return this->foo(i);
}
};
template <>
struct Foo<Y, X>
{
public:
unsigned foo(Y i) {
cout << "B" << endl;
return i.test_y();
}
};
class B : public A<Y, X> {
};
int main() {
B b = B();
Y y = Y();
cout << "Hello: " << b.bar(y) << endl;
return 0;
}

Edit: My first answer was incorrect.
When you instantiate A with classes Y and X the call of foo(T) generates an error because there no proper overloaded method defined in A. It suffices to declare a pure virtual method of foo(T) in A and implement this method in B.
template <typename T, typename U>
class A {
public:
virtual unsigned foo(T i) = 0;
unsigned foo(U i) { /* as above */ }
unsigned bar(T i) {
return foo(i); // calls abstract foo(T)
}
};
/* B is left untouched */
Compiling this generates this output:
B
Hello: 2

Related

variadic template only using type parameter

I would like to do something like this:
#include <iostream>
class a {
public:
a() : i(2) {}
template <typename ...ts>
void exec() {
f<ts...>();
std::cout << "a::()" << std::endl;
}
int i;
private:
template <typename t>
void f() {
i += t::i;
}
template <typename t, typename ...ts>
void f() {
f<t>();
f<t, ts...>();
}
};
struct b {
static const int i = -9;
};
struct c {
static const int i = 4;
};
int main()
{
a _a;
_a.exec<b,c>();
std::cout << _a.i << std::endl;
}
The idea is to get the same information from a group of classes, without the need of an object of each class.
Does anyone know if it is possible?
Thanks!
In case Your compiler does not support C++17:
template <typename ...ts>
void f() {
for ( const auto &j : { ts::i... } )
i += j;
}
In C++17, your class would simply be
class a {
public:
a() : i(2) {}
template <typename ...ts>
void exec() {
((i += ts::i), ...); // Folding expression // C++17
std::cout << "a::()" << std::endl;
}
int i;
};
Possible in C++11 too, but more verbose.
Reasons why your code is not compiling:
Syntax of specializing templates is a little different.
You need to put the most general case first.
You can't partially specialize functions, only classes.
Partial specialization is not allowed within classes, only in namespaces.
Here is an example for C++11.
#include <iostream>
template<typename t, typename ...ts>
class a {
public:
static constexpr int x = t::i + a<ts...>::x;
};
template<typename t>
class a<t> {
public:
static constexpr int x = 2 + t::i;
};
struct b {
static constexpr int i = -9;
};
struct c {
static constexpr int i = 4;
};
int main()
{
constexpr int result = a<b,c>::x;
std::cout << result << std::endl;
}
Remember that templates are calculated during compilation so, for optimization sake, it is a good idea to write them in a way that allows them to be constexpr.

Calling template function from inner class

I have a class like this to call a function depending on the type. I try to compile it, but have error error C2059 syntax error : 'template'
class A
{
call_1()
{
B<type> b;
b.template say(i);
}
template<class T>
struct B
{
template <typename T, typename I>
T say(I i) {
return word;
}
};
template<>
struct B<void>
{
template <typename T, typename I>
void say(I i) {
/**/
}
};
}
What am I doing wrong?
First, let's rewrite your example into something that is readable and is closer to being compilable, and we also print "1" or "2" in say() to know which function gets called:
#include <iostream>
using type = int;
class A {
void call_1() {
B<type> b;
int i = 0;
b.template say(i);
}
template<class T>
struct B
{
template <typename T, typename I>
T say(I i) {
std::cout << "1\n";
return T();
}
};
template<>
struct B<void>
{
template <typename T, typename I>
void say(I i) {
std::cout << "2\n";
}
};
};
OK, so first, you are trying to specialize B inside of A. This is not allowed, so let't move it outside of A:
using type = int;
class A {
void call_1() {
B<type> b;
int i = 0;
b.template say(i);
}
template<class T>
struct B
{
template <typename T, typename I>
T say(I i) {
std::cout << "1\n";
return T();
}
};
};
template<>
struct A::B<void>
{
template <typename T, typename I>
void say(I i) {
std::cout << "2\n";
}
};
Next up, you are using the same template parameter (T) in both B and say(). You don't need to repeat T, so let's delete it:
using type = int;
class A {
void call_1() {
B<type> b;
int i = 0;
b.template say(i);
}
template<class T>
struct B
{
template <typename I>
T say(I i) {
std::cout << "1\n";
return T();
}
};
};
template<>
struct A::B<void>
{
template <typename I>
void say(I i) {
std::cout << "2\n";
}
};
Finally, call_1() cannot be defined before the specialization of A::B, so we need to move it outside too:
using type = int;
class A {
void call_1();
template<class T>
struct B
{
template <typename I>
T say(I i) {
std::cout << "1\n";
return T();
}
};
};
template<>
struct A::B<void>
{
template <typename I>
void say(I i) {
std::cout << "2\n";
}
};
void A::call_1() {
B<type> b;
int i = 0;
b.template say(i);
}
This should now compile and do what you want. Calling call_1() will print 1. If you change the type from int to void:
using type = void;
it will print 2.

Template specialization using another class that implicit-converts to it

I don't know if this is possible, but I would like to understand better how this works.
Can a class implict convertsion operation be used to match a template parameter?
This is what I want to do.
#include <iostream>
template<typename T>
struct Value {
};
template<>
struct Value<int> {
static void printValue(int v) {
std::cout << v << std::endl;
}
};
struct Class1 {
int value;
};
/*
template<>
struct Value<Class1*> {
static void printValue(Class1* v) {
std::cout << v->value << std::endl;
}
};
*/
template<typename X>
struct ClassContainer {
ClassContainer(X *c) : _c(c) {}
operator X*() { return _c; }
X *_c;
};
template<typename X>
struct Value<ClassContainer<X>> {
static void printValue(ClassContainer<X> v) {
std::cout << static_cast<X*>(v)->value << std::endl;
}
};
template<typename X>
void doPrintValue(X v)
{
Value<X>::printValue(v);
}
int main(int argc, char *argv[])
{
doPrintValue(10);
Class1 *c = new Class1{ 20 };
//doPrintValue(c); // error C2039: 'printValue': is not a member of 'Value<X>'
ClassContainer<Class1> cc(c);
doPrintValue(cc);
std::cout << "PRESS ANY KEY TO CONTINUE";
std::cin.ignore();
}
ClassContainer has an implict conversion to X*. Is it possible to match ClassContainer passing only X*?
If you want the template class for pointers to behave like the template class for something else, just inherit:
template<typename T>
struct Value<T*> : Value<ClassContainer<T>> {};
It will inherit the public printValue function, which accepts a parameter that can be constructed from T*, and everything will be implicitly converted as expected.
See it all live here.

Call of member function using bind like functionality

How to implement a very simple edition of boost::bind, which does not bind arguments, but offer a way to call member function in c++ classes.
Here is my first try:
#include <iostream>
struct Foo {
void x(int i) { std::cout << "Foo " << i << std::endl; }
};
struct Bar {
void y(int i) { std::cout << "Bar " << i << std::endl; }
};
template<typename A1, typename I, typename M>
struct Binder {
Binder(I i, M m) : i_(i), m_(m) { }
void operator()(A1 a1) {
(i_->*m_)(a1);
}
I i_;
M m_;
};
template<typename A1, typename I, typename M>
Binder<A1, I, M> my_bind(I i, M m) {
return Binder<A1, I, M>(i, m);
}
int main(int argc, const char *argv[])
{
Foo foo;
Bar bar;
Binder<int, Foo*, void (Foo::*)(int)> b1 = my_bind<int>(&foo, &Foo::x);
Binder<int, Bar*, void (Bar::*)(int)> b2 = my_bind<int>(&bar, &Bar::y);
b1(1);
b2(2);
return 0;
}
The implementation above does work, and will print:
Foo 1
Bar 2
The problem is that the two invokes of my_bind returns objects of different types. How can I alter the program, such that my_bind will return a type which only depends on A1.
The problem is that the two invokes of my_bind returns objects of different types. How can I alter the program, such that my_bind will return a type which only depends on A1.
It is possible to do with type erasure.
In short:
Create Abstract class with interface you like to have. It may have some template parameters. (AbstractBinder in below code)
Create Concrete class which implements this interface. Concrete class may have more template arguments than interface. (Binder class below)
Create Holder class with template constructor - which creates Concrete class, but stores only pointer to it's base Abstract class. So, Holder class has only template parameters required for Abstract interface, while its constructor has all the rest template parameters required for Concrete class. (BinderHolder class below)
live demo
Usage:
int main()
{
Foo foo;
Bar bar;
BinderHolder<int> b1 = my_bind<int>(&foo, &Foo::x);
BinderHolder<int> b2 = my_bind<int>(&bar, &Bar::y);
b1(1);
b2(2);
}
Full code:
template<typename A1>
struct AbstractBinder
{
virtual void call(A1 a1)=0;
virtual AbstractBinder<A1> *clone()=0;
virtual ~AbstractBinder(){}
};
template<typename A1, typename I, typename M>
struct Binder : AbstractBinder<A1>
{
Binder(I i, M m) : i_(i), m_(m) { }
void call(A1 a1)
{
(i_->*m_)(a1);
}
virtual AbstractBinder<A1> *clone()
{
return new Binder(*this);
}
I i_;
M m_;
};
template<typename A1>
class BinderHolder
{
AbstractBinder<A1> *ptr;
BinderHolder &operator=(const BinderHolder&);
public:
template<typename I, typename M>
BinderHolder(I i, M m)
: ptr(new Binder<A1,I,M>(i,m))
{
}
BinderHolder(const BinderHolder &rhs)
: ptr(rhs.ptr->clone())
{
}
~BinderHolder()
{
delete ptr;
}
void operator()(A1 a1)
{
ptr->call(a1);
}
};
template<typename A1, typename I, typename M>
BinderHolder<A1> my_bind(I i, M m) {
return BinderHolder<A1>(i, m);
}
#include <iostream>
struct Foo {
void x(int i) { std::cout << "Foo " << i << std::endl; }
};
struct Bar {
void y(int i) { std::cout << "Bar " << i << std::endl; }
};
int main()
{
Foo foo;
Bar bar;
BinderHolder<int> b1 = my_bind<int>(&foo, &Foo::x);
BinderHolder<int> b2 = my_bind<int>(&bar, &Bar::y);
b1(1);
b2(2);
}
P.S. If you are sure, that all your Concrete classes would have same size, then you can replace heap allocations with placement new inside to fixed size buffer, and add static_assert for safety.

Is there a way to initialize a variable with a value based on the template type

I have a template class. The class has a private member variable which I like to have preset to a certain value which differs for each template type.
I was thinking about different constructors for different types, but since the constructor has no parameters I have no idea how to do it.
Is it possible anyway ?
Thanks,
Use a traits template and specialize it with the value. something like:
template <typename T, typename Traits = MyTraits<T> >
class MyClass {
public:
int Foo ()
{
return Traits::Value;
}
};
template <>
class MyTraits<SomeClass>
{
public:
static int Value = 1;
};
template <>
class MyTraits<AnotherClass>
{
public:
static int Value = 2;
};
You can do it with a specialization on the type, the easiest form is:
#include <iostream>
template <typename T>
struct make {
static int value() {
return -1; // default
}
};
template <>
struct make<int> {
static int value() {
return 1;
}
};
template <>
struct make<double> {
static int value() {
return 2;
}
};
template <typename T>
struct foo {
const int val;
foo() : val(make<T>::value()) {}
};
int main() {
std::cout << foo<int>().val << ", " << foo<double>().val << "\n";
}
But you could also arrange it as an overload:
#include <iostream>
int value(void *) {
return -1; // default
}
int value(double *) {
return 2;
}
int value (int *) {
return 1;
}
template <typename T>
struct foo {
const int val;
foo() : val(value(static_cast<T*>(nullptr))) {}
};
int main() {
std::cout << foo<int>().val << ", " << foo<double>().val << "\n";
}
You could put the mapping from template parameters to values into a helper class, giving you something like this:
template<typename T> struct foo_helper;
template<> struct foo_helper<int> { static int getValue() {return 1; } };
template<> struct foo_helper<float> { static int getValue() {return 2; } };
....
template<typename T> class foo
{
int m;
foo():m(foo_helper<T>::getValue()){}
};