Partial specialization of specific member functions - c++

#include <iostream>
template <typename T1, typename T2>
class B{
public:
void update(){ std::cerr<<__PRETTY_FUNCTION__<<std::endl; }
void func1(){ std::cerr<<__PRETTY_FUNCTION__<<std::endl; }
void func2(){ std::cerr<<__PRETTY_FUNCTION__<<std::endl; }
};
template <typename T1>
class B<T1, int>{
public:
void update(){ std::cerr<<__PRETTY_FUNCTION__<<"(specialization)"<<std::endl;}
};
int main(){
B<int, double> b1;
b1.update();
b1.func1();
B<int, int> b2;
b2.update();
//b2.func1();//there's no function 'func1' in B<int,int>
}
I want to specialize update function for specific template parameter(data type).
So I tried to specialize template class B but it seems that I have to implement whole member functions again.
Because other members are exactly same between specializations, reimplementing whole members look cumbersome.
Is there any workaround for this case?

Tag-dispatch the call to update:
template <typename> struct tag {};
template <typename T1, typename T2>
class B
{
public:
void update()
{
return update(tag<B>());
}
private:
template <typename U1>
void update(tag<B<U1, int> >)
{
// specialization
}
template <typename U1, typename U2>
void update(tag<B<U1, U2> >)
{
// normal
}
};
DEMO

Related

SFINAE and inheritance

I'm looking for a solution to a following problem:
#include <string>
class A
{
public:
template <typename T>
static typename std::enable_if<std::is_same<T, std::string>::value, void>::type foo(T val)
{
printf("std::string\n");
}
template<typename T, typename... Arg>
static void multiple(Arg&&... arg)
{
T::foo(arg...);
}
};
class B : public A
{
public:
template <typename T>
static typename std::enable_if<std::is_same<T, int>::value, void>::type foo(T val)
{
printf("int\n");
}
};
int main()
{
std::string a;
int b = 0;
A::multiple<B>(a, b);
}
All works fine if both foo methods are in the same class or I force foo from proper class (A::foo for std::string and B::foo for int), however I need more than one class, because base class must be extendable. I can't use simple specialization, because I need more SFINAE features like detect for std::pair, std::tuple etc. I also don't want to move foo methods from a class to a namespace. Do you have any Idea how can I solve this issue?
Here B::foo hides A::foo, you need a using:
class B : public A
{
public:
using A::foo;
template <typename T>
static typename std::enable_if<std::is_same<T, int>::value, void>::type foo(T val)
{
printf("int\n");
}
};
But
From namespace.udecl#15.sentence-1:
When a using-declarator brings declarations from a base class into a derived class, member functions and member function templates in the derived class override and/or hide member functions and member function templates with the same name, parameter-type-list, cv-qualification, and ref-qualifier (if any) in a base class (rather than conflicting)
Return type doesn't count, so you have to use std::enable_if in parameter:
class A
{
public:
template <typename T>
static void foo(T val, std::enable_if_t<std::is_same<T, std::string>::value, int> = 0)
{
printf("std::string\n");
}
};
class B : public A
{
public:
using A::foo;
template <typename T>
static void foo(T val, std::enable_if_t<std::is_same<T, int>::value, int> = 0)
{
printf("int\n");
}
};
Demo
Note: you also have typo for
template<typename T, typename... Arg>
static void multiple(Arg&&... arg)
{
T::foo(arg...); // B::foo(string, int)
}
which should be
template<typename T, typename... Arg>
static void multiple(Arg&&... arg)
{
(T::foo(arg), ...); // B::foo(string), B::foo(int)
}

Template argument deduction failed with typedef?

Considering the following couple of classes:
template <typename T1, typename T2>
class A{
public:
// ...
};
template<typename _T>
struct alias { typedef A<int,_T> intA; };
class B{
public:
// ...
template <typename _T> B& operator=(const typename alias<_T>::intA& _arg) { };
};
When I try to assign an object of class A<int,int> to an object of class B, I get the following compilation error:
template argument deduction/substitution failed: couldn't deduce template parameter ‘_T’
Is there an alternative way to use something of a typedef as the input argument to B::operator=()??
templated using might fix the issue
template <typename T1, typename T2>
class A{
public:
// ...
};
template<typename _T>
using alias = A<int,_T>;
class B{
public:
// ...
template <typename _T> B& operator=(const alias<_T>& ) { return *this; };
};
void f()
{
B b;
A<int, int> a;
b = a;
}
The problem is that intA is a dependant name. Templates cannot be deduced from dependant names. See for example: Dependent Types: Template argument deduction failed.
You are also missing the typename keyword.
You can either explicitly specify the type for the operator:
template <typename T1, typename T2>
struct A{ };
template<typename _T>
struct alias { typedef A<int,_T> intA; };
struct B
{
template <typename T> B& operator=(const typename alias<T>::intA& _arg) { };
};
int main()
{
A<int,int> a;
B b;
b.operator=<int>(a);
return 0;
}
or you can have a specific, non-dependant-name parameter using a templated alias (with or without a function):
template <typename T1, typename T2>
struct A{ };
template<class T>
using alias_int = A<int, T>;
struct alias
{
template<class T>
using intA = A<int, T>;
};
struct B
{
template <typename T> B& operator=(const alias_int<T>& _arg) { };
};
struct C
{
template <typename T> C& operator=(const alias::intA<T>& _arg) { };
};
int main()
{
A<int,int> a;
B b;
C c;
b = a;
c = a;
return 0;
}
I'm getting a different error (using g++ 5.4):
need ‘typename’ before ‘alias<_T>::intA’ because ‘alias<_T>’ is a dependent scope
and true enough the following compiles for me:
template <typename T1, typename T2>
class A{
public:
// ...
};
template<typename _T>
struct alias { typedef A<int,_T> intA; };
class B{
public:
// ...
template <typename _T> B& operator=(const typename alias<_T>::intA& _arg) { };
};
I think the reason is that alias<_T>::intA isn't an actual type but a templated typename.

Let template partial specializations share member functions with general instantiations

Suppose I have a class template Foo:
template<typename T, typename U>
struct Foo {
~Foo() = default;
// I want to reuse these methods for all instantiations of Foo
void bar() {}
void poi() {}
};
I want to specialize the destructor for any Foo<T, int>, but I want Foo<T, int> to share the other member functions with the general instantiations. But if I try to do:
template<typename T>
Foo<T, int>::~Foo()
{}
outside the class, it doesn't compile, the error being "invalid use of incomplete type struct Foo<T, int>". What does this error mean, and how can I achieve what I'm trying to do?
It is not possible to partially specialise a (non-templated) member function. You need to specialise the whole class.
One way to do what you want is to inherit the common functions.
template<typename T, typename U> struct MemberProvider
{
~MemberProvider() = default;
void bar() {};
void poi() {};
};
template<typename T, typename U>
struct Foo : public MemberProvider<T, U>
{
~Foo() = default;
// Optionally, you can still do this.
// However, note that these HIDE the inherited functions
void bar() {};
void poi() {};
};
template<typename T> struct Foo<T, int> : public MemberProvider<T, int>
{
~Foo() {};
};
Note you will also need to include any members acted on by the common functions in the inherited class.
However, I would suggest the above represents a code smell. If I saw code like the above in a production environment, I'd be contemplating the presence of a design flaw. Bear in mind that, when destructing such a class, the most derived destructor is invoked before base class destructors. MemberProvider is also not a polymorphic base.
You can explicitly specialize member functions of class templates, but you cannot partially specialize them - which is what you're trying to do.
If you need to partially specialize the destructor in a way that keeps the generic version defaulted, you'll have to partially specialize the whole class template:
template <class T, class U>
struct FooCommon {
~FooCommon() = default;
void bar() {}
void poi() {}
};
template <class T, class U>
struct Foo : FooCommon<T, U> { };
template <class T>
struct Foo<T, int> : FooCommon<T, int> {
~Foo() {
// special logic here
}
};
If foo and poi do things that depend on the template parameters, then you could extract your specialised destructor behaviour into a separate class, e.g.,
#include <iostream>
template <typename T, typename U>
struct Foo;
template <typename T, typename U>
struct FooDestructorTraits
{
static void destroy(Foo<T, U> * ptr)
{
std::cout << "default" << std::endl;
}
};
template <typename U>
struct FooDestructorTraits<int, U>
{
static void destroy(Foo<int, U> * ptr)
{
std::cout << "special" << std::endl;
}
};
template <typename T, typename U>
struct Foo
{
~Foo() noexcept { FooDestructorTraits<T, U>::destroy(this); }
void bar() {}
void baz() {}
};
int main() {
Foo<void, void> x;
Foo<int, void> y;
}

Template weirdness

I have five classes, declared so:
template <typename T>
class A {
void fn(X);
};
template <typename T>
class B {};
class C {};
class D {};
class X {};
and I have two instances declared so:
A<B<C>> abc;
A<B<D>> abd;
How can I templatize fn so that one must call abc.fn() with an object of type C and abd.fn() with an object of type D?
You can do a partial specialization of your class like this:
template <typename T> class A;
template <typename T> class B {};
template <typename T>
class A<B<T> > {
public:
void fn(T) { }
};
class C {};
class D {};
int main(int,char**)
{
A<B<C>> abc;
A<B<D>> abd;
abc.fn(C());
abd.fn(D());
return 0;
}
If you want it to work for any template, and not just B, you can partially specialize class A like this:
template <typename T,template <typename> class U>
class A<U<T> > {
public:
void fn(T) { }
};
This is not going to be too pretty.
template <typename T>
class B {public: typedef T type;};
template <typename T>
class A {
void fn(typename T::type X);
//void fn(...){} // would prevent an error if T does not have type.
};
Basically you save the type in a typedef and then use that in A. This would error out of course if B does the T of A does not have T::type.

Reducing number of template arguments for class

I have a method and two classes defined like this:
template<template<class X> class T>
void doSomething()
{
T<int> x;
}
template <class T>
class ClassWithOneArg
{
T t;
};
template <class T1, class T2>
class ClassWithTwoArgs
{
T1 t1;
T2 t2;
};
I can now
doSomething<ClassWithOneArg>();
but I cannot
doSomething<ClassWithTwoArgs>();
However, I'd like to pass ClassWithTwoArgs to doSomething, where T2 = double.
The only method I found is to create
template <class T1>
class ClassWithTwoArgs_Child
: public ClassWithTwoArgs<T1, double>
{
};
and then
doSomething<ClassWithTwoArgs_Child>();
This works, but in my concrete case all classes require a constructor argument and thus I have to create a constructor with this argument also in the _Child-class and pass it to the base which I really want to avoid.
Do you have an idea how to do that?
Thanks a lot!
Indirection is a solution. Instead of a template template parameter you pass a "meta function" -- a function that maps one type to another in form of a struct with a nested class template:
struct mf1 {
template<class Arg1>
struct eval {
typedef ClassTemplateWithOneArg<Arg1> type;
};
};
template<class Arg2>
struct mf2 {
template<class Arg1>
struct eval {
typedef ClassTemplateWithTwoArgs<Arg1,Arg2> type;
};
};
template<class MetaFunc>
void do_something()
{
typedef typename MetaFunc::template eval<int>::type clazztype;
clazztype x;
}
void foo() {
do_something<mf1>();
do_something<mf2<double> >();
}
In C++0x this could be reduced to a "template typedef":
template<class Arg1>
using NewClassTemplate = ClassTemplateWithTwoArgs<Arg1,double>;
which allows you to pass NewClassTemplate as a template template argument which also accepts only one template parameter.
There is no generic solution. Your best bet is
template<class T>
void doSomething()
{
T x;
}
template <class T>
class ClassWithOneArg
{
T t;
};
template <class T1, class T2 = double>
class ClassWithTwoArgs
{
T1 t1;
T2 t2;
};
int main(){
doSomething<ClassWithOneArg<int>>();
doSomething<ClassWithTwoArgs<int, double> >();
}
It seems that what you are after is similar to the rebinding of allocators (given an allocator, containers need to be able to produce an allocator for a different type - e.g std::list<int> might need a allocator<list_node<int> > from allocator<int>.
However, the class templates would have to be modified for this.
template<class T>
void doSomething(const T&)
{
typename T::template rebind_1st<int>::type x;
}
template <class T>
class ClassWithOneArg
{
T t;
public:
template <class U>
struct rebind_1st { typedef ClassWithOneArg<U> type; };
};
template <class T1, class T2>
class ClassWithTwoArgs
{
T1 t1;
T2 t2;
public:
template <class U>
struct rebind_1st { typedef ClassWithTwoArgs<U, T2> type; };
};
int main()
{
doSomething(ClassWithOneArg<char>());
doSomething(ClassWithTwoArgs<char, double>() );
}
Assuming you want declare template instantiations of the same class with a different type for the first template parameter, it appears a version of visitor's code is possible that doesn't require modifying original classes.
template <class T, class NewFirstArg>
struct rebind_1st;
template <template <class> class T, class Arg1, class NewFirstArg>
struct rebind_1st<T<Arg1>, NewFirstArg>
{
typedef T<NewFirstArg> type;
};
template <template <class, class> class T, class Arg1, class Arg2, class NewFirstArg>
struct rebind_1st<T<Arg1, Arg2>, NewFirstArg>
{
typedef T<NewFirstArg, Arg2> type;
};
template <class T>
void foo()
{
typename rebind_1st<T, int>::type x;
(void)x;
}
template <class T>
struct One{};
template <class T1, class T2>
struct Two{};
int main()
{
foo<One<char> >();
foo<Two<char, double> >();
}
This works with MSVC:
template<class T>
void doSomething()
{
T x;
}
// class definitions omitted...
void test() {
doSomething<ClassWithOneArg<int> >();
doSomething<ClassWIthTwoArgs<int, double> >();
}
I do not fully understand why you want to define the first parameter of your template template parameter to be int inside of doSomething. Looks like a "template smell" to me, since doSomething has to know a lot about its template template parameter.
Wouldn't it be cleaner to call doSomething the way i proposed? (But obviously i don't know the context of your calls).