How to use enable_if as template parameter with specialization - c++

Why the second function can't match the template in the class definition??
Class C {
template <typename T, typename T2 = T>
T val() const;
};
template <>
std::string C::val() const {
//OK
}
template <typename T, typename std::enable_if<std::is_arithmetic<T>::value>::type>
T C::val() const {
//Not OK
}
EDIT:
This is an overview of what I want to achieve. Basically I'm writing a function to parse and return an object based on the template type. I have some defined classes of my own, that is I have to parse with respect to their members. I also need to parse numerical types and to strings. So I wrote a specialized version for every of my defined classes. A version that parses to numerical types and return the given type (of course I have to make sure that the given type is numerical, hence the enable if)

To utilize SFINAE, use it in the template declaration, not specialization:
class C {
template <typename T, typename T2 = typename std::enable_if<std::is_arithmetic<T>::value>::type>
T val() const;
};
If you want to differentiate between arithemtic and non-arithmetic types (allowing both), you can utilize tagging:
class C {
public:
struct arithmetic_tag {};
struct non_arithmetic_tag {};
template <typename T>
T val(typename std::conditional<std::is_arithmetic<T>::value, arithmetic_tag, non_arithmetic_tag>::type tag = {}) const
{
return get_val<T>(tag);
}
private:
template <typename T>
T get_val(C::non_arithmetic_tag) const;
template <typename T>
T get_val(C::arithmetic_tag) const;
};
Or delegate the specialization to a helper class:
class C {
public:
template <typename T>
T val() const
{
return ValGetter<T>::get(this);
}
private:
template <typename T, bool is_arithmetic = std::is_arithmetic<T>::value>
struct ValGetter;
};
// Arithmetic
template <typename T>
struct C::ValGetter<T, true>
{
static T get(C const* c);
};
// Non-arithmetic
template <typename T>
struct C::ValGetter<T, false>
{
static T get(C const* c);
};
EDIT: partial specializations (bool parameter) do not work for methods, shown tagging and helper classes instead

Related

Call templated class with multiple parameters with single parameter only

I have a class with multiple template parameters; let us say it looks something like this:
template <class T, class B>
struct Vector2 : B
{
Vector2() noexcept;
constexpr explicit Vector2(T a) noexcept;
}
Template parameter B always depends on T. For example if T is float B will be XMFLOAT2, if T is int B will be XMINT2. For this I created a template specialization:
template class Vector2<float, XMFLOAT2>;
template class Vector2<int32_t, XMINT2>;
template class Vector2<uint32_t, XMUINT2>;
The problem is that since B always depends on T, I want to call Vector<float> for example, and the expression should expand to Vector<float, XMFLOAT2>.
I thought of doing a typealias, however, I wouldn't be sure how to accomplish this, since it would need to be specialized again.
template<class T> using Vector2 = Vector2<T, ??>;
That doesn't really make sense...
How can I call a class with multiple template parameters using just a single parameter with the others being deduced? Or is there a different approach?
You can create helper trait:
template <typename T>
struct vector_base_class;
template <> struct vector_base_class<float> { using type = XMFLOAT2; };
template <> struct vector_base_class<int32_t> { using type = XMINT2; };
template <> struct vector_base_class<uint32_t> { using type = XMUINT2; };
template <class T>
struct Vector2 : typename vector_base_class<T>::type
{
Vector2() noexcept;
constexpr explicit Vector2(T a) noexcept;
};
As mentioned in a comment, you are probably looking for a trait:
template <typename T>
struct xm_from_T;
template <class T, class B = typename xm_from_T<T>::type>
struct Vector2 : B
{
Vector2() noexcept;
constexpr explicit Vector2(T a) noexcept;
};
Then specialize the trait:
template <> struct xm_from_T<float> { using type = XMFLOAT2; };
template <> struct xm_from_T<int32_t> { using type = XMINT2; };
template <> struct xm_from_T<uint32_t> { using type = XMUINT2; };

How to check if a template argument is of a particular templated type (multiple type parameters)

I asked a question yesterday (How to find out if a type is a templated type of any type?) about how to check for a particular template argument when that argument is a templated class of any type. And the solution was something like this:
template <typename T>
struct Animal{};
template <typename T>
struct IsAnimalOfAnyType
{
constexpr bool value() { return false; }
};
template <typename T>
struct IsAnimalOfAnyType<Animal<T>>
{
constexpr bool value() { return true; }
};
However this works with single-argument templates, but I'm trying to do the following:
template <typename T, T integer, typename U>
struct Animal{};
template <typename T>
struct IsAnimalOfAnyType
{
constexpr bool value() { return false; }
};
template <typename T>
struct IsAnimalOfAnyType<Animal<T>>
{
constexpr bool value() { return true; }
};
/* Animal needs to be Animal<T, integer, U>,
but 'T', 'integer' and 'U' template arguments are not available here,
and if I have these arguments here
then IsAnimalOfAnyType is no longer a specialization and it won't compile
*/
As far as I understand it the difference is that struct Animal:
Has multiple template arguments
and
One of the arguments is not a type, but an integer
How to go about doing this?
You can declare all the template parameters required by Animal for the specialization.
template <typename T, T integer, typename U>
struct IsAnimalOfAnyType<Animal<T, integer, U>>
{
constexpr bool value() { return true; }
};
LIVE

How do two structures with same name and with different Template arguments work

Why does only first implementation work and not the other implementations specified below,
Can someone explain the way this template structure work and why others do not.
Template structures that work
template <typename T, typename U>
struct is_same
{
static const bool value = false;
};
template <typename T>
struct is_same<T, T>
{
static const bool value = true;
};
Template structures that do not work
template <typename T, typename U>
struct is_same<T, U>
{
static const bool value = false;
};
template <typename T>
struct is_same<T, T>
{
static const bool value = true;
};
One more that does not work
template <typename T, typename U>
struct is_same<T,U>
{
static const bool value = false;
};
template <typename T>
struct is_same
{
static const bool value = true;
};
And the Main Function
template <class A, class B>
bool IsSameClass() {
return is_same<A, B>::value;
}
int main()
{
bool ret = IsSameClass<P,C>();
}
You always start with a template definition:
template <class T, class U>
class is_same { ... };
Or, as Jarod42 reminded me, with a declaration:
template <class T, class U> class is_same;
Once there's a template, you can define a partial specialization by refining one or more of the template arguments, as in your example:
template <class T>
class is_same<T, T> { ...};
The specialization still takes two arguments, just like the original template, but they're the same type, so there's only one type name in the template <...> part of the definition.
In use, is_same<int, double> would match the template definition; is_same<int, int> would match the partial specialization.
You can also specify a full specialization. Not that you'd want to here, but following on with this example:
template <>
class is_same<void, void> { ... };
This would match, obviously, is_same<void, void>.
The reason that the other two attempts don't work is that neither of them has a template definition, only things that look like specializations. Without a definition there's nothing to specialize.
Note that this is a bit of an oversimplification. It's intended to address "why don't these things work", not "how can I specialize a template".
The syntax
template <typename T, typename U>
struct is_same<T, U>
{
static const bool value = false;
};
is used to create a specialization of a class template. In order to do that, you have to first declare the template or create its generic definition first.
template <typename T, typename U> struct is_same;
or
template <typename T, typename U>
struct is_same
{
static const bool value = false;
};
Even if you did that,
template <typename T, typename U>
struct is_same<T, U>
{
static const bool value = false;
};
is not a valid specialization since there is no way to decide which template to use when instantiated with is_same<int, float>.
Templates cannot be overloaded by using:
template <typename T, typename U>
struct is_same
{
static const bool value = false;
};
template <typename T>
struct is_same
{
static const bool value = true;
};
The language simply does not allow that.

Overloading template classes by template parameter number

Is it possible to have multiple versions of the same class which differ only in the number of template arguments they take?
For instance:
template<typename T>
class Blah {
public:
void operator()(T);
};
template<typename T, typename T2>
class Blah {
public:
void operator()(T, T2);
};
I'm trying to model functor type things which can take a variable number of arguments (up to the number of different templates that were written out).
The simplest answer would be to have just one template, with the maximum number you want to support and use void for a default type on all but the first type. Then you can use a partial specialization as needed:
template<typename T1, typename T2=void>
struct foo {
void operator()(T1, T2);
};
template <typename T1>
struct foo<T1, void> {
void operator()(T1);
};
int main() {
foo<int> test1;
foo<int,int> test2;
test1(0);
test2(1,1);
}
A template can have only one base definition. If you need a variable number of arguments and you don't want to use "null type" constructions as #awoodland suggests, and if you have a C++0x compiler, then you can use variadic templates:
template <typename ...Dummy> struct foo; // base case, never instantiated!
template <typename T> struct foo<T> { /*...*/ }; // partial spec. for one parameter
template <typename T, typename U> struct foo<T, U> { /*...*/ }; // ditto for two
This is untested code, I don't have a version of boost handy, but here goes anyway
#include "boost/tuple.h"
template <class T>
class Blah;
template <class T>
class Blah< boost::tuple<T> >
{
void operator()(T arg);
};
template <class T, class U>
class Blah< boost::tuple<T, U> >
{
void operator()(T arg1, U arg2);
};
etc. etc.

How can I write a function template for all types with a particular type trait?

Consider the following example:
struct Scanner
{
template <typename T>
T get();
};
template <>
string Scanner::get()
{
return string("string");
}
template <>
int Scanner::get()
{
return 10;
}
int main()
{
Scanner scanner;
string s = scanner.get<string>();
int i = scanner.get<int>();
}
The Scanner class is used to extract tokens from some source. The above code works fine, but fails when I try to get other integral types like a char or an unsigned int. The code to read these types is exactly the same as the code to read an int. I could just duplicate the code for all other integral types I'd like to read, but I'd rather define one function template for all integral types.
I've tried the following:
struct Scanner
{
template <typename T>
typename enable_if<boost::is_integral<T>, T>::type get();
};
Which works like a charm, but I am unsure how to get Scanner::get<string>() to function again. So, how can I write code so that I can do scanner.get<string>() and scanner.get<any integral type>() and have a single definition to read all integral types?
Update: bonus question: What if I want to accept more than one range of classes based on some traits? For example: how should I approach this problem if I want to have three get functions that accept (i) integral types (ii) floating point types (iii) strings, respectively.
struct Scanner
{
template <typename T>
typename boost::enable_if<boost::is_integral<T>, T>::type get()
{
return 10;
}
template <typename T>
typename boost::disable_if<boost::is_integral<T>, std::string>::type get()
{
return "string";
}
};
Update "What if I want to accept more than one range of classes based on some traits?"
struct Scanner
{
template <typename T>
typename boost::enable_if<boost::is_integral<T>, T>::type get()
{
return 10;
}
template <typename T>
typename boost::enable_if<boost::is_floating_point<T>, T>::type get()
{
return 11.5;
}
template <typename T>
std::string get(
typename boost::disable_if<boost::is_floating_point<T>, T>::type* = 0,
typename boost::disable_if<boost::is_integral<T>, T>::type* = 0)
{
return std::string("string");
}
};
Defer to another template. Here's the general pattern for what you want:
template <typename T, bool HasTrait = false>
struct scanner_impl;
template <typename T>
struct scanner_impl
{
// Implement as though the trait is false
};
template <typename T>
struct scanner_impl<true>
{
// Implement as though the trait is true
};
// This is the one the user uses
template <typename T>
struct scanner : scanner_impl<T, typename has_my_trait<T>::value>
{
};