type_traits for std Container?
It does not exist.
You can create your own trait if you know the set of container types that should be supported :
template<class T>
struct is_container
{
static const bool value = false;
};
template<>
template<class T, class Alloc>
struct is_container<std::vector<T, Alloc>>
{
static const bool value = true;
};
// ... same specializations for other containers.
And you use it like other traits:
cout << is_container<std::vector<int>>::value << endl;
cout << is_container<int>::value << endl;
See it here.
Note that usually you should pass iterators to your functions, not containers. So you keep your code container-independent and much more generic.
How to write a type trait method
The template parameter T
of member templates shadows the template parameter T
of class template. Give them another name; and specify default value for template parameter of min()
, otherwise they can't be deduced. e.g.
template<typename X>
using isComplex = std::is_same<X, std::complex<typename X::value_type>>;
template <typename X = T>
typename std::enable_if<isComplex<X>::value>::type min() {
std::cout << "Min for complex" << std::endl;
}
template <typename X = T>
typename std::enable_if<std::is_arithmetic<X>::value>::type min() {
std::cout << "Min for arithmetic values." << std::endl;
}
LIVE
Determine if a type is an STL container at compile time
First, you define your primary template, which will have a member which is false in the default case:
template <typename T>
struct is_cont {
static const bool value = false;
};
Then you will define partial specializations for your container types which have a value of true instead:
template <typename T,typename Alloc>
struct is_cont<std::vector<T,Alloc> > {
static const bool value = true;
};
Then for a type X that you want to check, use it like
if (is_cont<X>::value) { ... }
sfinae to detect containers: failure for std:array
This is not SFINAE but regular template specialisation. Your std::array is not recognised because a value of type std::size_t (which ist std::array's second argument) is not a typename.
You can change your check for array specifically:
template <typename T, std::size_t N> struct is_container<std::array<T,N>> : std::true_type { };
template <typename... Ts> struct is_container<std::vector<Ts...>> : std::true_type { };
If you actually want to use SFINAE to check for anything that behaves like a container, you could check for the existance of std::begin, std::end, std::size for that type.
How to specialize template for containers and enums
The reason that the second call to the default implementation is very simple.
Simply work out by hand how the parameters get deduced for the container version template's parameters:
template<typename Container>
struct wrapper<Container, typename Container::value_type>
You are instantiating the following template:
wrapper<std::vector<int>>
So:
1) Container
is a std::vector<int>
2) Container::value_type
is int
Therefore this specialization becomes:
struct wrapper<std::vector<int>, int>
However you are invoking only:
wrapper<std::vector<int>, void>
because void
is the default value for the second template parameter, so this matches the wrong specialization.
The solution is very simple, the container specialization should simply be:
#include <type_traits>
template<typename Container>
struct wrapper<Container, std::void_t<typename Container::value_type>> {
std::void_t
is C++17, there are other question on stackoverflow.com that explain how to implement it for earlier C++ standards. Complete example:
#include <vector>
#include <iostream>
#include <type_traits>
enum En {};
template <typename T, typename = void>
struct wrapper {
static T getValue()
{
std::cout<<"\n WRAPPER DEFAULT VERSION IS CALLED.\n";
return T();
}
};
template<typename T>
struct wrapper<T,typename std::enable_if<std::is_enum<T>::value>::type>{
static T getValue()
{
std::cout<<"\n WRAPPER ENUM VERSION IS CALLED.\n";
return T();
}
};
template<typename Container>
struct wrapper<Container, std::void_t<typename Container::value_type>> {
static Container getValue()
{
std::cout<<"\n WRAPPER CONTAINER VERSION IS CALLED.\n";
return Container();
}
};
int main()
{
//En is an enum type
En en = (En) wrapper<En>::getValue(); //Prints ENUM VERSION
std::vector<int> vec;
vec = wrapper<std::vector<int>>::getValue(); //Prints DEFAULT VERSION
}
Result:
WRAPPER ENUM VERSION IS CALLED.
WRAPPER CONTAINER VERSION IS CALLED.
How to implement is_enum_class type trait?
Based on your +T{}
test:
Option #1:
Expression SFINAE in trailing return type:
#include <type_traits>
template <typename T>
auto test(int) -> decltype((void)+T{}, std::false_type{});
template <typename T>
auto test(...) -> std::true_type;
template <typename T>
using is_enum_class = std::integral_constant<bool, decltype(test<T>(0))::value && std::is_enum<T>::value>;
DEMO
Option #2:
In void_t-fashion:
template <typename T, typename V = void>
struct test : std::false_type {};
template <typename T>
struct test<T, decltype((void)+T{})> : std::true_type {};
template <typename T>
using is_enum_class = std::integral_constant<bool, !test<T>::value && std::is_enum<T>::value>;
DEMO 2
Tests:
enum class EC { a, b };
enum E { c, d };
int main()
{
static_assert(is_enum_class<EC>::value, "!");
static_assert(!is_enum_class<E>::value, "!");
static_assert(!is_enum_class<int>::value, "!");
}
Dealing with inconsistent typedefs in generic code
Maybe can be done in a simpler way... anyway, I propose a tag dispatching / SFINAE solution.
First of all, a simple recursive tag
struct
template <std::size_t N>
struct tag : public tag<N-1u>
{ };
template <>
struct tag<0u>
{ };
to avoid ambiguities in cases more that one of the possible type names are defined.
Then a template function (only declared) for every type you want extract from the possible types; one for type
template <typename T, std::void_t<typename T::type>* = nullptr>
typename T::type getType (tag<0u>);
one for this_type
template <typename T, std::void_t<typename T::this_type>* = nullptr>
typename T::this_type getType (tag<1u>);
one for ThisType
template <typename T, std::void_t<typename T::ThisType>* = nullptr>
typename T::ThisType getType (tag<2u>);
and one (to be a little silly) for MySillyTypeName
template <typename T, std::void_t<typename T::MySillyTypeName>* = nullptr>
typename T::MySillyTypeName getType (tag<3u>);
Observe that the number of the tag
are differents: this avoid the possible ambiguity and give a priority order for the names.
Now a trivial struct that uses getType()
to extract the required type
template <typename T, typename U = decltype(getType<T>(tag<100u>()))>
struct GetType { using type = U; };
The following is a full compiling C++17 example
#include <type_traits>
template <std::size_t N>
struct tag : public tag<N-1u>
{ };
template <>
struct tag<0u>
{ };
template <typename T, std::void_t<typename T::type>* = nullptr>
typename T::type getType (tag<0u>);
template <typename T, std::void_t<typename T::this_type>* = nullptr>
typename T::this_type getType (tag<1u>);
template <typename T, std::void_t<typename T::ThisType>* = nullptr>
typename T::ThisType getType (tag<2u>);
template <typename T, std::void_t<typename T::MySillyTypeName>* = nullptr>
typename T::MySillyTypeName getType (tag<3u>);
template <typename T, typename U = decltype(getType<T>(tag<100u>()))>
struct GetType { using type = U; };
struct foo1 { using type = short; };
struct foo2 { using this_type = int; };
struct foo3 { using ThisType = long; };
struct foo4 { using MySillyTypeName = long long; };
int main()
{
static_assert( std::is_same_v<short, GetType<foo1>::type> );
static_assert( std::is_same_v<int, GetType<foo2>::type> );
static_assert( std::is_same_v<long, GetType<foo3>::type> );
static_assert( std::is_same_v<long long, GetType<foo4>::type> );
}
Related Topics
C++ Access Static Members Using Null Pointer
Is C/C++ Bool Type Always Guaranteed to Be 0 or 1 When Typecast'Ed to Int
Altering Dll Search Path for Static Linked Dll
How to Make Std::Vector's Operator[] Compile Doing Bounds Checking in Debug But Not in Release
Undefined Symbols "Vtable for ..." and "Typeinfo For..."
Regex Replace with Callback in C++11
C++ Program Converts Fahrenheit to Celsius
How to Access the Underlying Container of Stl Container Adaptors
Is There a Difference Between Universal References and Forwarding References
Declaring the Array Size with a Non-Constant Variable
Floating Point Format for Std::Ostream
How to Check If C++ Compiler Uses Ieee 754 Floating Point Standard
Range-For-Loops and Std::Vector<Bool>
C++ Delete Vector, Objects, Free Memory
Using Qsocketnotifier to Select on a Char Device
Why Are Python Programs Often Slower Than the Equivalent Program Written in C or C++