I am trying to trade some run-time checks for compile-time checks to identify the base class of an object with template specializations.
The code compiles fine, but I can't figure out why the enable_if statement always end up invalid or equal to void because I'm always landing on the base template struct.
#include <iostream>
#include <type_traits>
using namespace std;
struct BaseOne {};
struct DerivedOne : BaseOne {};
struct BaseTwo {};
struct DerivedTwo : BaseTwo {};
struct Default {};
template<typename T, typename = void>
struct get_category_impl {
static constexpr int value = 0;
};
template<typename T>
struct get_category_impl<T, typename enable_if<is_base_of<BaseOne, T>::value, T>::type> {
static constexpr int value = 1;
};
template<typename T>
struct get_category_impl<T, typename enable_if<is_base_of<BaseTwo, T>::value, T>::type> {
static constexpr int value = 2;
};
template<typename T>
constexpr int get_category = get_category_impl<T>::value;
int main() {
cout << get_category<BaseOne> << "\n"; // prints 0
cout << get_category<DerivedOne> << "\n"; // prints 0
cout << get_category<BaseTwo> << "\n"; // prints 0
cout << get_category<DerivedTwo> << "\n"; // prints 0
cout << get_category<Default> << "\n"; // prints 0
}
The second parameter to enable_if doesn't need to be specified. If you do specify it, it needs to somehow resolve to void. Since you've specified the second parameter as T, this doesn't work.
Instead, just do this:
template<typename T>
struct get_category_impl<T, typename enable_if<is_base_of<BaseOne, T>::value>::type> {
// ^ No T
static constexpr int value = 1;
};
and similarly for the other specialization.
Here's a demo.
Related
This question already has answers here:
Checking for existence of an (overloaded) member function
(3 answers)
Closed 2 days ago.
I've written a trait that detects whether a class has a certain public member function via std::is_member_function_pointer:
class Test1
{
public:
void method();
};
class Test2
{
public:
void method();
void method(int);
};
template <typename T, typename = void>
struct HasMethod : std::false_type
{
};
template <typename T>
struct HasMethod<T, std::enable_if_t<std::is_member_function_pointer<decltype(&T::method)>::value>>
: std::true_type
{
};
int main()
{
std::cout << HasMethod<Test1>::value << std::endl; // prints 1
std::cout << HasMethod<Test2>::value << std::endl; // prints 0
}
However it seems that when the function is overloaded, then std::is_member_function_pointer is not able to detect it, as shown with Test2.
Is there a way to make it work regardless of whether the method is overloaded?
To detect existence of class members usual approach is to use std::void_t template. You can do it like this:
#include <iostream>
#include <type_traits>
class Test1
{
public:
void method();
};
class Test2
{
public:
void method();
void method(int);
};
template <typename T, typename = void>
struct HasMethod : std::false_type
{
};
// Use method(0) to detect void method(int) overload
// You don't even need std::void_t in this case, since method returns void anyway, but for any other return type you will need it
template <typename T>
struct HasMethod<T, std::void_t<decltype(std::declval<T>().method())>>
: std::true_type
{
};
int main()
{
std::cout << HasMethod<Test1>::value << std::endl; // prints 1
std::cout << HasMethod<Test2>::value << std::endl; // prints 1
}
Ok. After some thinking I found solution. We can use fact that declval(&T::method) will fail, if there is more than one overload, to detect if we have at least one overload, by adding another one. Here is solution. It is quite verbose, but I was unable to reduce it. At least it works.
#include <iostream>
#include <type_traits>
class Test
{
};
class Test1
{
public:
void method();
};
class Test2
{
public:
void method();
void method(int);
};
class Test3
{
public:
using method = int;
};
class Test4
{
public:
int method;
};
template<typename T, typename = void>
struct HasSingleOverload : std::false_type {};
template<typename T>
struct HasSingleOverload<T, std::void_t<decltype(&T::method)>> : std::true_type {};
template<typename T, typename = void>
struct IsMemberFunction : std::false_type {};
template<typename T>
struct IsMemberFunction<T, std::enable_if_t<std::is_member_function_pointer<decltype(&T::method)>::value>> : std::true_type {};
template<typename T, typename = void>
struct IsType : std::false_type {};
template<typename T>
struct IsType<T, std::void_t<typename T::method>> : std::true_type {};
struct HasOverload {
void method();
};
template<typename T>
struct CheckOverload : T, HasOverload {
};
template<typename T>
using HasConflict = std::bool_constant<!HasSingleOverload<CheckOverload<T>>::value>;
template<typename T>
using HasAnyOverload = std::conditional_t<HasSingleOverload<T>::value || IsType<T>::value, IsMemberFunction<T>, HasConflict<T>>;
int main()
{
std::cout << HasAnyOverload<Test>::value << std::endl; // prints 0
std::cout << HasAnyOverload<Test1>::value << std::endl; // prints 1
std::cout << HasAnyOverload<Test2>::value << std::endl; // prints 1
std::cout << HasAnyOverload<Test3>::value << std::endl; // prints 0
std::cout << HasAnyOverload<Test4>::value << std::endl; // prints 0
}
I have some classes which need to define a template which can be used in generic code parts as type later.
In real world code the forwarded templates have a lot more parameters and it is not really nice to read the code.
Q: Is it possible to define the template in some syntax instead of writing it as alias template as given in the following example? I simple would avoid repeating of all the template parameters two times of each alias declaration.
The real world template also have some non type template parameters so simply using <PARMS...> will not work.
Example:
#include <iostream>
template < typename T>
struct A
{
static void Do(T t) { std::cout << "A " << t << std::endl;}
};
template < typename T>
struct B
{
static void Do(T t) { std::cout << "B " << t << std::endl;}
};
struct UseA
{
// using the alias template works as expected, but...
template < typename T>
using USE = A<T>;
// is there any chance to write something like:
// using USE = A;
// to simply avoid replication of template parameters?
};
struct UseB
{
template < typename T>
using USE = B<T>;
};
int main()
{
UseA::USE<int>::Do(1);
UseB::USE<std::string>::Do("Hallo");
}
What you are asking cannot be done. You always have to define the whole type list. The reason is, that one could have default overloads for the same type. For example, in the following A<int, 3>, A<int> and A<> are all valid. The compiler does not know which one you want:
template <class T, int Value = 42>
struct A {};
auto test() {
auto a = A<int, 3>{};
auto b = A<int>{};
auto c = A<>{};
}
If you don't want to write the type lists, I would recommend you to switch to templatizing more of your classes, so they don't need to know about the implementation details. Like:
#include <iostream>
template < typename T>
struct A
{
static void Do(T t) { std::cout << "A " << t << std::endl;}
};
template < typename T>
struct B
{
static void Do(T t) { std::cout << "B " << t << std::endl;}
};
template < typename T>
struct Use
{
using USE = T;
};
int main()
{
Use<A<int>>::USE::Do(1);
Use<B<std::string>>::USE::Do("Hallo");
}
Or alternatively, use containers for your non template type values:
#include <iostream>
template < int Value >
struct INT
{
static constexpr int value = Value;
};
template < bool Value >
struct BOOL
{
static constexpr bool value = Value;
};
template < typename T, typename Value >
struct A
{
static void Do(T t) { std::cout << "A " << t << Value::value << std::endl;}
};
template < typename T, typename Value>
struct B
{
static void Do(T t) { if (Value::value) std::cout << "B " << t << std::endl;}
};
template <template<typename...> class T, typename ...Param>
using USE = T<Param...>;
int main()
{
USE<A, int, INT<42>>::Do(1);
USE<B, std::string, BOOL<true>>::Do("Hallo");
}
Could you please explain, why integral_constant and constexpr approaches in the following example result in different behaviours?
#include <iostream>
using namespace std;
struct Logger
{
// template<typename Type>
// using IsRawString =
// std::integral_constant<bool, std::is_same<const char*, Type>::value || std::is_same<char*, Type>::value>;
template<typename Type>
constexpr bool IsRawString()
{
return std::is_same<const char*, Type>::value || std::is_same<char*, Type>::value;
}
template<typename Type, typename Enable = void>
struct Traits
{
static const int Index = 1;
};
template<typename Type>
struct Traits<Type, std::enable_if_t<IsRawString<Type>()>>
{
static const int Index = 2;
};
template<typename Type>
struct Traits<Type, std::enable_if_t<std::is_pointer<Type>::value && !IsRawString<Type>()>>
{
static const int Index = 3;
};
};
int main()
{
cout << Logger::Traits<int>::Index << endl
<< Logger::Traits<char*>::Index << endl
<< Logger::Traits<const char*>::Index << endl
<< Logger::Traits<void*>::Index << endl;
return 0;
}
integral_constant approach https://ideone.com/WQy71r:
1
2
2
3
constexpr approach https://ideone.com/wPiM1m:
1
1
1
1
If I remove Logger struct and use Traits from the global scope, both approaches give the same result as for integral_constant inside struct (https://ideone.com/WGVuXE https://ideone.com/OpTbDm).
It happens because your IsRawString() declared as non static and it require class object for to be called.
When compiler try to generate Logger::Traits<char*>::Index and Logger::Traits<const char*>::Index it use SFINAE(http://en.cppreference.com/w/cpp/language/sfinae) and reject wrong variants with your constexpr function. The first Traits form with template<typename Type, typename Enable = void> generates only. Try declare IsRawString() as static member function
I have a class which allows for a vector to be created holding any type or class. However I'd like to add additional functionality for numerical types.
template <>
class Vec<double> : public VecBase<double>
{
// == METHODS ==
public:
// -- Constructors & Destructors --
explicit Vec(const unsigned long long t_size);
virtual ~Vec();
// -- Operators --
friend Vec<double> operator+(const Vec<double>&, const double);
// -- Methods --
double sum();
... etc.
I have partially specialised the class template to allow overloading of mathematical operators for double specialisation. I'd now like to extend this specialisation to int as well, but rather than copy the specialisation replacing double with int, is there a way to add it into the specialisation list?
That is, is there any way to allow for:
template<>
class Vec<double (or) int>
Cheers!
I suppose you can use a boolean default value, like in foo struct in the following example
#include <iostream>
template <typename>
struct isSpecialType
{ static constexpr bool value { false }; };
template <>
struct isSpecialType<int>
{ static constexpr bool value { true }; };
template <>
struct isSpecialType<double>
{ static constexpr bool value { true }; };
template <typename T, bool = isSpecialType<T>::value>
struct foo;
template <typename T>
struct foo<T, true>
{ static constexpr bool value { true }; };
template <typename T>
struct foo<T, false>
{ static constexpr bool value { false }; };
int main()
{
std::cout << "- void value: " << foo<void>::value << std::endl;
std::cout << "- bool value: " << foo<bool>::value << std::endl;
std::cout << "- int value: " << foo<int>::value << std::endl;
std::cout << "- double value: " << foo<double>::value << std::endl;
}
The idea is define a sort of type traits (isSpecialType) to choose the selected types (int and double, in your example) with a booleand value that is false in the generic implementation and true in the specializations.
template <typename>
struct isSpecialType
{ static constexpr bool value { false }; };
template <>
struct isSpecialType<int>
{ static constexpr bool value { true }; };
template <>
struct isSpecialType<double>
{ static constexpr bool value { true }; };
Next you have to declare the foo struct (class Vec, in your question) with a supplemental bool template value with the isSpecialType<T>::value default value
template <typename T, bool = isSpecialType<T>::value>
struct foo;
Last, you have to implement two partially specialized version of foo: the first one with the boolean true value
template <typename T>
struct foo<T, true>
{ static constexpr bool value { true }; };
corresponding to the specialized version of your Vec; the one with the false boolean value
template <typename T>
struct foo<T, false>
{ static constexpr bool value { false }; };
corresponding to the generic version of your Vec.
Another point: my example is C++11 or newer code; if you want a C++98 version, you have only to define the bool values as const (instead constexpr) and initialize they whit the C++98 style; I mean
static bool const bool value = true;
instead of
static constexpr bool value { true };
There sure is but you might find this already done in http://en.cppreference.com/w/cpp/numeric/valarray
have a look at std::enable_if and std::is_integral and std::is_floating_point. (copied from cplusplus.com)
// enable_if example: two ways of using enable_if
#include <iostream>
#include <type_traits>
// 1. the return type (bool) is only valid if T is an integral type:
template <class T>
typename std::enable_if<std::is_integral<T>::value,bool>::type
is_odd (T i) {return bool(i%2);}
// 2. the second template argument is only valid if T is an integral type:
template < class T,
class = typename std::enable_if<std::is_integral<T>::value>::type>
bool is_even (T i) {return !bool(i%2);}
int main() {
short int i = 1; // code does not compile if type of i is not integral
std::cout << std::boolalpha;
std::cout << "i is odd: " << is_odd(i) << std::endl;
std::cout << "i is even: " << is_even(i) << std::endl;
return 0;
}
I have same idea with #max66, but you can use a helper function to do this a bit easier.
#include <iostream>
#include <type_traits>
// helper
template <typename ...Ts>
struct allowed_types
{
template <typename T>
using check = std::disjunction<std::is_same<T, Ts>...>;
template <typename T>
inline static constexpr bool check_v = check<T>::value;
};
// usage
template <typename T, bool = allowed_types<double, float>::check_v<T>>
struct foo;
template <typename T>
struct foo<T, true> // for double and float
{
inline static constexpr size_t value = 1;
};
template <typename T>
struct foo<T, false> // for other types
{
inline static constexpr size_t value = 2;
};
int main()
{
std::cout << foo<float>::value << '\n'; // 1
std::cout << foo<double>::value << '\n'; // 1
std::cout << foo<int>::value << '\n'; // 2
std::cout << foo<char>::value << '\n'; // 2
}
Just put any set of types you need.
For example:
template <typename T, bool = allowed_types<char, int, std::vector<int>>::check_v<T>>
EDIT:
If you need to split your specializations more than on 2 groups, then you need to use enable_if approach.
With helper from above it could be written like this:
// default, for any type
template <typename T, typename = void>
struct foo
{
inline static constexpr size_t value = 1;
};
// for double and float
template <typename T>
struct foo<T, std::enable_if_t<allowed_types<double, float>::check_v<T>>>
{
inline static constexpr size_t value = 2;
};
// for int and char
template <typename T>
struct foo<T, std::enable_if_t<allowed_types<int, char>::check_v<T>>>
{
inline static constexpr size_t value = 3;
};
int main()
{
std::cout << foo<bool>::value << '\n'; // 1
std::cout << foo<double>::value << '\n'; // 2
std::cout << foo<float>::value << '\n'; // 2
std::cout << foo<int>::value << '\n'; // 3
std::cout << foo<char>::value << '\n'; // 3
}
I want accepted_dense_vector<??>::value to return 'true' when I give template parameters in the form:
C<T> when C is uvector, dynamic_array and T is std::is_arithmetic.
std::array<T,S> when T is std::is_arithmetic.
container_reference<C> when accepted_dense_vector<C>::value is 'true'.
All works fine, but in derived, fully instantiated classes A, C, D, I want to remove the workaround parent definition.
How can I do this?
#include <iostream>
#include <array>
using namespace std;
// Definition of used types.
template<typename T> struct dynamic_array {};
template<typename T> struct uvector {};
struct A : public uvector<double> { typedef uvector<double> parent; };
struct C : public A { typedef A parent; };
struct D : public std::array<double,5> { typedef std::array<double,5> parent; };
template<typename T> struct B : public uvector<T> { typedef uvector<T> parent; };
template<typename T> struct container_reference {};
// Catches 'false', A, C, D ======== HERE IT IS !!! ========
template<typename C> struct accepted_dense_vector
{
template<typename C1 = C>
static typename std::enable_if<accepted_dense_vector<typename C1::parent>::value, std::true_type>::type helper(const C&);
static std::false_type helper(...);
constexpr static bool value = decltype(helper(std::declval<C>()))::value;
};
// Catches C<T>
template<template<typename> class C, typename T>
struct accepted_dense_vector<C<T>>
{
constexpr static bool value = std::is_arithmetic<T>::value &&
(std::is_base_of<uvector<T>, C<T>>::value || std::is_base_of<dynamic_array<T>, C<T>>::value);
};
// Catches std::array<T,S>
template<typename T, size_t S>
struct accepted_dense_vector<std::array<T,S>>
{ constexpr static bool value = std::is_arithmetic<T>::value; };
// Catches container_reference<C>
template<typename C>
struct accepted_dense_vector<container_reference<C>>
{ constexpr static bool value = accepted_dense_vector<C>::value; };
int main()
{ // Tests!
cout << accepted_dense_vector<std::array<double, 5>>::value << endl;
cout << accepted_dense_vector<uvector<double>>::value << endl;
cout << accepted_dense_vector<A>::value << endl;
cout << accepted_dense_vector<D>::value << endl;
cout << accepted_dense_vector<B<int>>::value << endl;
cout << accepted_dense_vector<container_reference<uvector<double>>>::value << endl;
cout << accepted_dense_vector<int>::value << endl;
return 0;
}
You can go further with your solution. First, you do not need std::enable_if. The code will be more readable. Then, instead of using std::true_type, use a custom struct that will call accepted_dense_vector:
template<typename T>
struct forwarder
{
constexpr static bool value = accepted_dense_vector<T>::value;
};
template<typename T>
static forwarder<uvector<T>> inherit_uvector(uvector<T>*);
static std::false_type inherit_uvector(...);
Then, to know whether the type satisfy accepted... by inheritance of uvector<T>, just use the following line:
constexpr static bool is_uvector = decltype(inherit_uvector(new C()))::value;
// C is the template parameter of accepted_dense_vector
How does this works ?
if C inherits uvector<T>, the first static function is choosed, and decltype will return forwarder<uvector<T>>. Then, 'calling' ::value on it will call accepted....
else, if C does not inherits uvector<T>, the second function is choosed, and decltype will return std::false_type.
The same trick can be used with the other inheritances. Also, B<T> was catched with the second specialization of accepted.... Thus, the ::parent typedef is not needed anymore.
Here is the full code (live here):
#include <iostream>
#include <array>
#include <string>
using namespace std;
// Definition of used types.
template<typename T> struct uvector {};
template<typename T> struct dynamic_array {};
template<typename T> struct container_reference {};
struct A : public uvector<double> {};
template<typename T> struct B : public uvector<T> {};
struct C : public A {};
struct D : public std::array<double,5> {};
struct E : public uvector<string> {};
struct F : public std::array<string,16> {};
// Catches 'false', A, C, D ======== HERE IT IS !!! ========
template<typename C>
struct accepted_dense_vector
{
template<typename T>
struct forwarder
{
constexpr static bool value = accepted_dense_vector<T>::value;
};
// uvector
template<typename T>
static forwarder<uvector<T>> inherit_uvector(uvector<T>*);
static std::false_type inherit_uvector(...);
// std::array
template<typename T, size_t S>
static forwarder<std::array<T,S>> inherit_stdarray(std::array<T,S>*);
static std::false_type inherit_stdarray(...);
// same for dynamic_array<T>
constexpr static bool is_uvector = decltype(inherit_uvector(new C()))::value;
constexpr static bool is_stdarray = decltype(inherit_stdarray(new C()))::value;
constexpr static bool value = is_uvector || is_stdarray;
};
// Catches C<T>
// /!\ Also catches anything which is template<typename> class (for example, B)
// => B::parent is not needed, because of the use of std::is_base_of
template<template<typename> class C, typename T>
struct accepted_dense_vector<C<T>>
{
constexpr static bool value = std::is_arithmetic<T>::value &&
(std::is_base_of<uvector<T>, C<T>>::value || std::is_base_of<dynamic_array<T>, C<T>>::value);
};
// Catches std::array<T,S>
template<typename T, size_t S>
struct accepted_dense_vector<std::array<T,S>>
{
constexpr static bool value = std::is_arithmetic<T>::value;
};
// Catches container_reference<C>
template<typename C>
struct accepted_dense_vector<container_reference<C>>
{
constexpr static bool value = accepted_dense_vector<C>::value;
};
int main()
{ // Tests!
cout << "array = " << accepted_dense_vector<std::array<double, 5>>::value << endl;
cout << "uvector = " << accepted_dense_vector<uvector<double>>::value << endl;
cout << "ref = " << accepted_dense_vector<container_reference<uvector<double>>>::value << endl;
cout << "A = " << accepted_dense_vector<A>::value << endl;
cout << "B = " << accepted_dense_vector<B<int>>::value << endl;
cout << "C = " << accepted_dense_vector<C>::value << endl;
cout << "D = " << accepted_dense_vector<D>::value << endl;
cout << "E = " << accepted_dense_vector<E>::value << endl;
cout << "F = " << accepted_dense_vector<F>::value << endl;
cout << "int = " << accepted_dense_vector<int>::value << endl;
return 0;
}