Can we specialize a template class for a templated class?
template<>
class Storage8<MyClass<T>>
{
—
—
};
Here MyClass is a templated class. Is the above valied? Where do we have to mention
template for MyClass?
This should work:
// class templae for Storage8. It can just be a forward declaration
template <typename T> class Storage8;
// or a default defintion.
template <typename T> class Storage8
{
};
// Class templae for MyClass
template <typename T>
class MyClass
{
};
// Specialization of Storage8 for MyClass<T>
template <typename T>
class Storage8<MyClass<T>>
{
};
You totally can. Example:
#include <vector>
using std::vector;
template<typename T>
struct X {
static const int A = 0;
};
template<typename U>
struct X<vector<U> > {
static const int A = 1;
};
int main() {
static_assert(X<vector<int> >::A == 1, "fail");
return 0;
}
Related
There is a way to make a partial template specialization for a forward-declared (incomplete) type (answer).
But after seeing the mentioned question I wondered if it is possible to define a partial specialization for a class template using an incomplete nested (and possibly private) class.
In C++ currently one can't forward-declare a nested class without defining the class:
class undefined;
class undefined::foo; // impossibru
With some hacks I made a working code (for a sake of research): https://godbolt.org/z/9W8nfhx8P
#include <iostream>
template <typename T, typename = T>
struct specialize;
// workaround to get to a nested foo
template <typename T, typename...>
struct mem_foo
{
// error: 'struct undefined::foo' is private within this context
using type = typename T::foo;
};
class undefined;
// class undefined::foo; // impossibru
template <typename MemFoo>
struct specialize<typename mem_foo<undefined, MemFoo>::type, MemFoo>
{
void operator()(const MemFoo &f) const
{
// this will compile however
std::cout << f.name << std::endl;
}
};
#include <string>
class undefined
{
private: // will not compile without friend
struct foo{
std::string name = "John Cena";
};
friend struct mem_foo<undefined, foo>; // ugly
// friend struct specialize<foo>; // this is irrelevant, but would be nicer than mem_foo
public:
static foo get() { return {}; }
};
int main()
{
specialize</*undefined::foo*/decltype(undefined::get())>{}(undefined::get());
return 0;
}
But for a private types an ugly friend is used. friend struct specialize<undefined::foo>; would be more semantically appealing.
Is there another or more elegant solution?
A more elegant/less convoluted solution: https://godbolt.org/z/3vrfPWP5f
#include <iostream>
template <typename T, typename = T>
struct specialize;
template <typename T, typename ...>
struct defer_instantiation
{
using type = T;
};
template <typename T, typename ... R>
using defer_instantiation_t = typename defer_instantiation<T, R...>::type;
class undefined;
// class undefined::foo; // impossibru
template <typename MemFoo>
struct specialize<typename defer_instantiation_t<undefined, MemFoo>::foo, MemFoo>
{
void operator()(const MemFoo &f) const
{
// this will compile however
std::cout << f.name << std::endl;
}
};
#include <string>
class undefined
{
private:
struct foo{
std::string name = "John Cena";
};
friend struct specialize<foo>; // this is irrelevant, but would be nicer than mem_foo
public:
static foo get() { return {}; }
};
int main()
{
specialize</*undefined::foo*/decltype(undefined::get())>{}(undefined::get());
return 0;
}
It allowes to reference yet non-existing member types of a yet incomplete class via a deferred template instantiation.
I have ::Class1 and ::Class2, I'd like to create template function that gets either first or second one and then based on selected class use other classes defined in different namespace i.e. NameSpace::Class1, NameSpace::Class2. Is there way to do it in C++?
For example:
namespace NameSpace
{
class Class1 {}; class Class2 {};
}
template <class T> // example Class1 or Class2
void f(T object) {
NameSpace::T obj; // Something like this, but it doesn't work
}
NameSpace::T doesn't work as T isn't the name Class1 or Class2, it represents the type. Templates don't work on textual substitution like that.
You could provide a trait to translate between the type in the global namespace and that from NameSpace:
struct Class1{}; struct Class2 {};
namespace NameSpace
{
class Class1 {}; class Class2 {};
template <typename T> struct translate;
template<> struct translate<::Class1> {
using type = Class1;
};
template<> struct translate<::Class2> {
using type = Class2;
};
template <typename T>
using translate_t = typename translate<T>::type;
}
You would use this like so:
template <class T>
void f(T object) {
using Translated = NameSpace::translate_t<T>;
Translated obj;
}
You can specialize a struct to map one type to another
template <typename T>
struct FromType {};
template <>
struct FromType<Class1>
{
typedef ns::Class1 type;
}
template <>
struct FromType<Class2>
{
typedef ns::Class2 type;
}
This can then be used in a template function by referring to the resulting type as
typename FromType<T>::type
Note that this will also give a compile error if you attempt to use a type other than Class1 or Class2.
Just omnit the NameSpace in NameSpace::T. The namespace must be defined in the function f call. The following example compiles.
namespace NameSpace
{
class Class1 {}; class Class2 {};
}
template <class T> // example Class1 or Class2
void f(T object) {
T obj;
}
int main()
{
NameSpace::Class1 x;
f(x);
return 0;
}
I have a struct within a class like this
template <class T>
class a {
struct b {
int var;
b *foo(const T&);
};
int var;
};
and I want to define foo outside of struct b.
How do I do that?
template <class T>
typename a<T>::b* a<T>::b::foo(const T&)
{
//code
}
I have two classes class A and class B both of them are template classes for a member function in A I want it to act in a special way when the type of A is B
and in a normal way for any other types I don't know how to do this ?
template <class B>
class B
{
private:
T m;
public:
...... any member functions
}
template <class T>
class A
{
private:
T var;
public:
void doSomething();
};
template <class T>
void A<T>::doSomething(){...........//implementation}
template <class T>
void A<B<T>>::doSomething(){................//different implementation}
You can specialize A this way:
template <class T>
class A<B<T>> {
// ...
};
This is an instance of partial template specialization.
If you refuse to specialize the entire class, you can defer the work from A<T>::doSomething() to a function doSomethingForA<T>(A &) that would be partially specialized, and that would possibly be friend of A<T>.
Hope this solves your problem:
#include <iostream>
template <typename T>
struct B {};
template <typename T> struct A;
template <typename T>
void doSomething(T&) { std::cout << "General\n"; }
template <typename T>
void doSomething(A<B<T>>&) { std::cout << "Special\n"; }
template <typename T>
struct A {
void doSomething() {
::doSomething(*this);
}
};
int main()
{
A<int> general;
A<B<int>> special;
general.doSomething();
special.doSomething();
}
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.