What is the correct syntax to fully specialize a template class, to an already defined specialization?
E.g., in the code below, how can I declare A<2> as an alias to A<0>?
template <int I>
struct A;
template <>
struct A<0> { int x; };
template <>
struct A<1> { int y; };
template <>
using A<2> = A<0>; // error: expected unqualified-id before 'using'
Note: for the code above, it would be enough to specialize A<1> and define the non-specialized A like A<0>:
template <int I>
struct A { int x; };
template <>
struct A<1> { int y; };
but this would not work if you a more complex situation, where for a set of values you have a specialization, for another set of values another specialization, etc...
Not sure I understood your problem, since you described it by your attempt to solve it.
Here is my approach to achieve similar result:
struct Ax { int x; };
struct Ay { int y; };
template <int I>
struct Aselector;
template<>
struct Aselector<0>{
using type = Ax;
};
template<>
struct Aselector<1>{
using type = Ay;
};
template<>
struct Aselector<2>{
using type = Ax;
};
template <int I>
using A = typename Aselector<I>::type;
https://godbolt.org/z/fGxcoE5xd
Version using type traits is shorter:
#include <type_traits>
struct Ax { int x; };
struct Ay { int y; };
template <int I>
using A = std::conditional_t<I == 1, Ay, std::enable_if_t<I >= 0 && I <= 2, Ax>>;
https://godbolt.org/z/vMqPahfEo
more complex situation, where for a set of values you have a specialization, for another set of values another specialization, etc...
A level of indirection can help here.
#include <iostream>
#include <type_traits>
template <size_t I> struct selector { static const size_t value = I; };
template <> struct selector<2> { static const size_t value = 0;};
template <size_t J> struct Foo_impl { int x; };
template <> struct Foo_impl<1> { int y; };
template <size_t I> using Foo = Foo_impl< selector<I>::value >;
int main() {
std::cout << std::is_same_v< Foo<0> , Foo<2> > << "\n";
Foo<0> f0;
f0.x = 42;
Foo<1> f1;
f1.y = 0;
Foo<2> f2;
f2.x = 123;
}
Foo<0> and Foo<2> are the same type. You can add more specializations to Foo_impl and you can add more mapping between the template argument I and actual index of specialization J.
The other answers should work, I just wanted to offer a SFINAE based solution:
#include <iostream>
template <int I, typename = void>
struct A;
template <int I>
struct A<I, std::enable_if_t<I == 0 || I == 2>> { int x; };
template <typename Conditional>
struct A<1, Conditional> { int y; };
int main()
{
std::cout << A<0>{}.x << std::endl;
std::cout << A<1>{}.y << std::endl;
std::cout << A<2>{}.x << std::endl;
return 0;
}
Caveat: while this code allows A<0> and A<2> to share an implementation, they will not be identical types. Eg: std::is_same_v<A<0>, A<2>> will return true for the other answers, but false for this one.
Related
I have existing template class A,B that cannot be changed.
template<class T>
struct A{static void F(){}};
template<int I>
struct B{};
I want to specialize A only when T is B<I> and 1<=I<=5.
If A can be changed, it would be like the following :
template<class T,class = void>//added second param
struct A{static void F(){}};
template<int I>
struct B{};
template<int I>
struct A< B<I>, std::enable_if_t<(1<=I&&I<=5)> >{static void F(){}};
int main(){
A<B<0>>::F();//call origin ver
A<B<1>>::F();//call specialized ver
A<B<10>>::F();//call origin ver
}
Is it possible?
You could redirect the specialization into inheriting from a conditional implementation
template<class T>
struct A{
static void F() { std::cout << "default\n"; }
};
template<int I>
struct B{};
struct tag{};
template<int I>
struct ABImpl {
static void F() { std::cout << "specialized for " << I << '\n'; }
};
template<int I>
struct A<B<I>> : std::conditional_t<1 <= I && I <= 5, ABImpl<I>, A<tag>> {};
The tag is a dummy that' simply used for grabbing a default implementation without risk of conflict.
Live Example
Add one layer of indirection.
template <class T>
struct SpecializedA {
//... specialized implementation
};
template<class T,class = void>//added second param
struct MyA_impl{
using type = A<T>;
};
template<int I>
struct MyA_impl< B<I>, std::enable_if_t<(1<=I&&I<=5)> >{
using type = SpecializedA<B<I>>;
};
template <typename T>
using MyA = typename MyA_impl<T>::type;
With that we get
MyA<B<0>> == A<B<0>>
MyA<B<1>> == SpecializedA<B<1>>
MyA<int> == A<int>
The program below does not compile if I uncomment the line containing foo<double>(), because B<double> depends on A<double>, which is an incomplete type.
#include <iostream>
using namespace std;
template <class T> struct A; // forward declaration (incomplete)
template <> struct A<int> {}; // specialized for int
template <class T> struct B : A<T> { int foo() {return 0;} }; // derived class, general definition inherits from A
template <> struct B<bool> { int foo() {return 1;} }; // derived class, does not inherit from A
template <class T> int foo() { B<T> b; return b.foo(); } // to be called if B<T> is valid
int main()
{
cout << foo<int>() << "\n"; // print 0
cout << foo<bool>() << "\n"; // print 1
// cout << foo<double>() << "\n"; // this line would generate a compile error
}
I would like a way to overload the function foo so that if B<T> is not a valid type, then an alternative version of the function foo is called.
I.e. I would like to have a way to define the overload
template <class T> int foo() { return -1; } // to be called if B<T> is not valid
I can also wrap the function foo inside a struct, if that helps. Is there a way to do that in C++03?
Remembering your analogue question and the answer from Quentin, I see that the problem is that B<T> can be (apparently) complete when A<T> is incomplete.
The only way I see (sorry: only C++11 at the moment) is impose that the general B<T> is defined only if A<T> is defined (trasforming it in a partial specialization); in the following way
template <typename T, bool = is_complete<A<T>>::value>
struct B;
template <typename T>
struct B<T, true> : A<T>
{ int foo() {return 0;} };
template <>
struct B<bool>
{ int foo() {return 1;} };
If you can modify B in this way, the solution is simple (using again the is_complete developed by Quentin).
The following is a working example
#include <iostream>
#include <type_traits>
template <typename T, std::size_t = sizeof(T)>
std::true_type is_complete_impl(T *);
std::false_type is_complete_impl(...);
template <typename T>
using is_complete = decltype(is_complete_impl(std::declval<T*>()));
template <typename>
struct A;
template <>
struct A<int>
{ };
template <typename T, bool = is_complete<A<T>>::value>
struct B;
template <typename T>
struct B<T, true> : A<T>
{ int foo() {return 0;} };
template <>
struct B<bool>
{ int foo() {return 1;} };
template <typename T>
typename std::enable_if<true == is_complete<B<T>>::value, int>::type foo()
{ B<T> b; return b.foo(); }
template <typename T>
typename std::enable_if<false == is_complete<B<T>>::value, int>::type foo()
{ return 2; }
int main()
{
std::cout << foo<int>() << "\n"; // print 0
std::cout << foo<bool>() << "\n"; // print 1
std::cout << foo<double>() << "\n"; // print 2
}
I have a .h file with all my templates in it and a .cpp file with my main.
Part of .h templates:
template<int N, int P>
struct BOUND {
static inline int eval(int v) {
//...
return 1;
};
};
template<class K>
struct VAL_x {
static inline int eval(int v) {
//...
return 1;
};
};
template<int L, class K>
struct LIT {
static inline int eval(int v) {
//...
return 1;
};
};
template<class A, class B, class K>
struct ADD {
static inline int comp_b(int v){
// HERE check if class A is LIT or VAL_x
//...
return 2;
};
};
Here is how I call in my main() this template:
int main() {
typedef ADD<VAL_x<BOUND<2,3> >, LIT<2, BOUND<2,3> >, BOUND<2,3> > FORM;
FORM exec_form;
int y = 2;
int bounds = exec_form.comp_b(y);
return 0;
}
How can I know in ADD::comp() function of my struct, if an argument that was passed is instance of a specific class (e.g. LIT<> )? Those arguments can be passed in any order (e.g all arguments could be LIT, or only the second one)
NOTE: there are also other structs apart from VAL_x , LIT, BOUND and ADD.
Option #1
Introduce a separate trait for each class template of interest (C++03 doesn't help here much).
template <bool B> struct bool_constant { static const bool value = B; };
template <bool B> const bool bool_constant<B>::value;
template <typename T> struct is_LIT : bool_constant<false> {};
template <int L, int M> struct is_LIT<LIT<L, M> > : bool_constant<true> {};
template <typename T> struct is_VAL_x : bool_constant<false> {};
template <int K> struct is_VAL_x<VAL_x<K> > : bool_constant<true> {};
template <class A, class B>
struct ADD
{
static inline int comp_b(int v)
{
if (is_LIT<A>::value && is_VAL_x<B>::value)
{
}
return 2;
}
};
DEMO
Option #2
Use a generic custom trait, whose specialization detects if the type passed is an instantiation of the specified template-template parameter (it is if the specialization matches, i.e., T is an instantiation of class template X):
template <template <int> class X, typename T>
struct is_template { static const bool value = false; };
template <template <int> class X, int N>
struct is_template<X, X<N> > { static const bool value = true; };
template <typename A, typename B>
struct ADD
{
static inline int comp_b(int v)
{
if (is_template<VAL_x, A>::value && is_template<LIT, B>::value)
{
}
return 2;
}
};
DEMO 2
Option #3
Use tag-dispatching, possibly add overloads for other class templates that return true/false, making it similar to Option #1. This solution also relies on overload resolution, that prefers more specialized function templates over those less constrained/generic.
template <typename T> struct tag {};
template <typename A, typename B>
struct ADD
{
static inline int comp_b(int v)
{
return comp_b(v, tag<A>(), tag<B>());
}
template <int M, int N>
static inline int comp_b(int v, tag<LIT<M> >, tag<VAL_x<N> >)
{
return 1;
}
template <typename T, typename U>
static inline int comp_b(int v, tag<T>, tag<U>)
{
return 2;
}
};
DEMO 3
You could do it like this:
#include <typeinfo>
...
template<class A, class B>
struct ADD {
static inline int comp_b(int v){
// HERE check if class A is LIT or VAL_x
std::cout << ( typeid(A)==typeid(VAL_x) ) << '\n';
return 2;
};
};
where I am using std::type_info, which will print 1, for true evaluation.
Or, with c++11, you could do:
#include <type_traits>
...
if (std::is_same<A, VAL_x>::value)
std::cout << "they are same!\n";
However, you could overload a function or such. Make sure you read this: How do I check my template class is of a specific classtype? and this How to check for the type of a template parameter?
The code bellow works as expected(it prints out 2.1):
#include <iostream>
template<typename T>
struct number {
T n_;
number(T n)
: n_{n}
{}
};
template<typename A, typename B>
auto operator+(number<A> a, number<B> b) -> number<decltype(a.n_+b.n_)> {
return a.n_+b.n_;
}
int main() {
number<int> a{1};
number<double> b{1.1};
number<double> c{a+b};
std::cout << c.n_ << std::endl;
}
However, it requires C++11. Assuming I'm restricted to C++03, is it possible to achieve the same behaviour? (i.e.: Make the return type of operator+ use the most precise representation for member n_?)
You could use Boost.TypeTraits:
template <typename A, typename B>
typename boost::common_type<A, B>::type operator+(number<A> a, number<B> b) {
return a.n_ + b.n_;
}
Or write your own trait for this purpose based on the rules of the usual arithmetic conversions. I hope this one is satisfactory:
// 1. A and B undergo integral promotions.
// 2. If the rank of B is higher than A then they are swapped.
// 3. The result is A, if:
// i. A or B or both are floating point, or
// ii. A and B have the same signedness, or
// iii. A is unsigned, or
// iv. The size of A is greater than the size of B.
// 4. Otherwise, the result is make_unsigned<A>.
namespace detail {
using namespace std::tr1;
template <typename T> struct promote { typedef T type; };
template <> struct promote<bool> { typedef int type; };
template <> struct promote<char> { typedef int type; };
template <> struct promote<signed char> { typedef int type; };
template <> struct promote<unsigned char> { typedef int type; };
template <> struct promote<short> { typedef int type; };
template <> struct promote<unsigned short> { typedef int type; };
template <typename> struct rank;
template <> struct rank<int> { enum { value = 0 }; };
template <> struct rank<unsigned> { enum { value = 0 }; };
template <> struct rank<long> { enum { value = 1 }; };
template <> struct rank<unsigned long> { enum { value = 1 }; };
template <> struct rank<long long> { enum { value = 2 }; };
template <> struct rank<unsigned long long> { enum { value = 2 }; };
template <> struct rank<float> { enum { value = 3 }; };
template <> struct rank<double> { enum { value = 4 }; };
template <> struct rank<long double> { enum { value = 5 }; };
template <typename> struct make_unsigned;
template <> struct make_unsigned<int> { typedef unsigned type; };
template <> struct make_unsigned<long> { typedef unsigned long type; };
template <> struct make_unsigned<long long> { typedef unsigned long long type; };
// 4.
template < typename A
, typename B
, bool Is_floating_point_or_same_signs_or_A_is_unsigned_or_bigger >
struct common_type_impl {
typedef A type;
};
template <typename A, typename B>
struct common_type_impl<A, B, false>
: make_unsigned<A> {};
// 3.
template <typename A, typename B, bool A_is_higher>
struct common_type_swap
: common_type_impl< A
, B
, is_floating_point<A>::value || is_floating_point<B>::value
|| (is_signed<A>::value == is_signed<B>::value)
|| is_unsigned<A>::value || (sizeof(A) > sizeof(B))
> {};
template <typename A, typename B>
struct common_type_swap<A, B, false>
: common_type_swap<B, A, true> {};
// 2.
template <typename A, typename B>
struct common_type
: common_type_swap<A, B, (rank<A>::value > rank<B>::value)> {};
}
// 1.
template <typename A, typename B>
struct common_type
: detail::common_type< typename detail::promote<A>::type
, typename detail::promote<B>::type > {};
It is at least more readable than Boost's. For brevity it relies on is_floating_point, is_signed, and is_unsigned from TR1, but these are easy to implement yourself if you can't use TR1.
DEMO
N.B. It doesn't give the same result as std::common_type when A and B are the same type and ranked below int. This is by design. It gives the same result as decltype(A{} + B{}). I should've given it a different name when I realized this but I couldn't be bothered.
I have the following traits class
template<typename T> struct FeatureType;
and I'm using it like so, which works fine:
class Foo { };
template<> struct FeatureType<Foo> {
typedef int value;
};
template<typename T> class Bar { };
template<typename T> struct FeatureType<Bar<T>> {
typedef T value;
};
Is there a way to extent this implementation for generic types to those that have more than one type parameter (unlike Bar above)? The following does not work
template<typename A, typename B> class Huh { };
template<typename A, typename B> struct FeatureType<Huh<A,B>> {
typedef A value;
};
Thanks!
Regular templates
Regular templates do not overload on their template parameters, but you can partially specialize them on arbitrary many template parameters. Your code should work as long as you put ; behind every struct declaration/definition. (note it is a custom to denote nested types inside templates as type, and values as value):
#include <iostream>
template<typename T>
struct FeatureType;
class Foo { };
template<> struct FeatureType<Foo>
{
typedef int type;
type value;
};
template<typename T> class Bar { };
template<typename T> struct FeatureType<Bar<T>>
{
typedef T type;
type value;
};
template<typename A, typename B> class Huh {};
template<typename A, typename B>
struct FeatureType< Huh<A,B> >
{
typedef A type;
type value;
};
int main()
{
FeatureType<Foo> f0;
f0.value = 0;
FeatureType< Bar<int> > f1;
f1.value = 1;
FeatureType< Huh<int, int> > f2;
f2.value = 2;
std::cout << f0.value << f1.value << f2.value;
}
Output on LiveWorkSpace (gcc 4.7.2)
Note: even if you have multiple formal template parameters (A, B, or as many as you like), the actual template is partially specialized for the single class Huh<A, B>
Variadic templates
If you actually want to have multiple versions of FeatureType taking a different number of template parameters, you need to use variadic templates (C++11)
#include <iostream>
template<typename... Args>
struct FeatureType;
template<> struct FeatureType<int>
{
typedef int type;
type value;
};
template<typename T> struct FeatureType< T >
{
typedef T type;
type value;
};
template<typename A, typename B>
struct FeatureType< A, B >
{
typedef A type;
type value;
};
int main()
{
FeatureType< int > f0;
f0.value = 0;
FeatureType< int > f1;
f1.value = 1;
FeatureType< int, int > f2;
f2.value = 2;
std::cout << f0.value << f1.value << f2.value;
}
Output on LiveWorkSpace
I'm not sure exactly what you tried, but you sure can specialize with as many template arguments as you like:
template <typename A, typename B>
class foo { };
template <typename T>
struct feature_type {};
template <typename A, typename B>
struct feature_type<foo<A,B>> {
typedef A type1;
typedef A type2;
};
int main(int argc, const char* argv[])
{
typename feature_type<foo<int,char>>::type1 x;
typename feature_type<foo<int,char>>::type2 y;
return 0;
}
See it in action.