Using specialization to test the existence of a value within a class - c++

Lets say I have the following class:
struct Color
{
enum {
blue,
red,
unknown
};
};
I would like to test in compile time if the class Color has the unknown value.
I thought the following could work but it is currently returning an incorrect result.
#include <iostream>
template <typename T, typename U = T>
struct test
{
static constexpr bool value = false;
};
template <typename T>
struct test<T, decltype(T::unknown)>
{
static constexpr bool value = true;
};
int main()
{
std::cout << test<Color>::value << std::endl; //prints 0
}
Could you provide some insight on how I can accomplish the check?
Or what is wrong with my example?
Thanks

When you write test<Color>, we first instantiate the primary specialization of test, which includes filling in the default template parameters. So we get test<Color, Color>.
Next, we try to see if any of the specializations match. The one specialization is for test<Color, Color::<unnamed>>. This doesn't match, so we stick with the primary!
This type mismatch is why void_t exists - so that our defaulted template parameter on the primary can match the specialization:
template <typename T, typename U = void>
// ~~~~~~~
struct test
{
static constexpr bool value = false;
};
template <typename T>
struct test<T, void_t<decltype(T::unknown)>>
// ~~~~~~~ ~
{
static constexpr bool value = true;
};
Also, prefer to just inherit from false_type and true_type, respectively.

Related

How to make traits to accept a parameter pack?

I define some type traits like this:
template <typename T>
struct has_something
{
static constexpr bool value = false;
};
template <>
struct has_something<int>
{
static constexpr bool value = true;
};
template <typename T>
constexpr bool has_something_v = has_something<T>::value;
And a function template which is has_something_v is a requirement for function parameter:
template <typename T, typename = std::enable_if_t<has_something_v<T>>>
void some_function(const T temp)
{
}
When i call it with wrong type:
struct wrong_type
{
};
void f ()
{
some_function(wrong_type());
}
compiler give me a proper error message:
/tmp/untitled/main.cpp:23: candidate template ignored: requirement 'has_something_v<wrong_type>' was not satisfied [with T = wrong_type]
but when i called with another template function:
template <typename ...T, typename = std::enable_if_t<has_something_v<T...>>>
void some_function(const T... args)
{
(some_function(args), ...);
}
void f ()
{
some_function(1, 2, a());
}
compiler give me really bad and confusing error message because i don't have a parameter pack acceptable traits :
Compiler error message
And if i remove std::enable_if from last template function, everything work fine until i send a wrong_type type to function which result is in crashing program.
For parameter pack, i wrote this:
template <typename ...T>
struct has_something
{
static bool value;
static constexpr bool c(T... args)
{
value = (args && ...);
return value;
}
};
template <>
struct has_something<int>
{
static constexpr bool value = true;
};
template <typename ...T>
const bool has_something_v = has_something<T...>::value;
But it still fail.
How could i write a acceptable parameter pack type traits ?
If you want to make the trait accept a parameter pack and it's value be true only when the parameter pack has a single type and that type is int you need to change only little on your code:
#include <iostream>
#include <type_traits>
template <typename...T>
struct has_something : std::false_type {};
template <>
struct has_something<int> : std::true_type {};
template <typename... T>
constexpr bool has_something_v = has_something<T...>::value;
int main() {
std::cout << has_something_v<int>;
std::cout << has_something_v<double>;
std::cout << has_something_v<int,double>;
}
Using std::true_type and std::false_type makes traits a bit shorter to write. I only had to make the trait accept a parameter pack, the specialization can stay the same.
Last but not least you should pick a better name. For example is_one_int would be much better than something.
PS: SFINAE can be used to create compiler errors, but often a simple static_assert is the better choice to get a clean message:
template <typename...T>
void foo(T...args) {
static_assert( is_one_int_v<T...>, " put nice message here");
}
SFINAE is the tool of choice when you want to disambiguate different overloads, but if a function should simply fail without alternative then a static_assert is simpler.

Detect whether a type is a vector of enum

I get an error:
error: default template arguments may not be used in partial specializations
in the following code:
#include <iostream>
#include <type_traits>
#include <vector>
enum class MyEnum
{
aaa,
bbb,
};
template<class T>
struct is_vector_enum
{
using type = T ;
constexpr static bool value = false;
};
template<class T, class std::enable_if<std::is_enum<T>::value>::type* = nullptr> // Error ....
struct is_vector_enum<std::vector<T>>
{
using type = std::vector<T> ;
constexpr static bool value = true;
};
int main()
{
std::cout << "is_vector_enum: " << is_vector_enum<std::vector<MyEnum>>::value << std::endl;
}
The purpose is to detect whether a type is a vector of enum.
How should I fix this code?
Your primary template and your specialization need to have the same number of template parameters. At the moment, your primary has 1:
template<class T>
struct is_vector_enum
and your specialization has 2:
template<class T, class std::enable_if<std::is_enum<T>::value>::type* = nullptr>
struct is_vector_enum<std::vector<T>>
The typical way to do this in C++17 is to provide a dummy 2nd template parameter to the primary, that defaults to void, to then let you do the SFINAE in the second parameter:
template <class T, class Enable=void>
struct is_vector_enum { /* ... */ };
template <class T>
struct is_vector_enum<std::vector<T>, std::enable_if_t<std::is_enum_v<T>>> { /* ... */ };
A different way entirely to do this would be:
template <typename T, std::enable_if_t<std::is_enum_v<T>, int> = 0>
std::true_type impl(std::vector<T> const&);
template <typename T>
std::false_type impl(T const&);
template <typename U>
using is_vector_enum = decltype(impl(std::declval<T>()));
Note that the impl functions here are not defined, and are not intended to be invoked.
Specializations are allowed to have a different number of template parameters than the primary. In fact, this happens quite often. However, as the error indicates, you are not allowed to give any of them default arguments.
That aside, I prefer simplicity, when possible.
template <typename T>
struct is_vector_enum : std::false_type { };
template <typename T>
struct is_vector_enum<std::vector<T>> : std::is_enum<T> { };

C++ template argument without a name

I have come across the following code snippet:
template <typename T, typename = void>
struct test {
int t = sizeof(T);
};
I know that in typename = void, void is a default argument but it doesn't have a name! What is it useful for and what does it even mean?
This is used for specializations in conjunction with SFINAE. Doing this allows you to have code like
template <typename T, typename = void>
struct test {
int t = sizeof(T);
};
template <typename T>
struct test<T, std::enable_if_t<std::is_integral_v<T>>> {
// ^^ this part "fills in" the void ^^
int t = 42;
};
template <typename T>
struct test<T, std::enable_if_t<std::is_floating_point_v<T>>> {
// ^^ this part "fills in" the void ^^
int t = 21;
};
int main()
{
test<int> i;
std::cout << i.t << "\n";
test<double> d;
std::cout << d.t;
}
which outputs
42
21
Without the typename = void, we would not be able to add these specializations because there would be no second parameter the enable_if_t part could "fill in".
I personally like to see it as a case of default template (type)argument to sfinae out overloads which does not meet certain criteria.
First thing first, giving name to a default template argument is just fine, so the following is correct:
template <typename T, typename sometype= void>
struct test {
int t = sizeof(T);
};
In the above case , clearly the type argument sometype is not used anywhere in the struct test. But what if instead of setting default value to void, we set it using some compile time conditions so that the template function is only valid for integral types like so ?
(borrowing code's from nathan's answer)
template <typename T>
struct test<T, typename sometype = std::enable_if_t<std::is_integral_v<T>>> {
int t = 42;
};
If the T has type integral then sometype is defined otherwise the given template is ignored making use of sfinae.
Additionally, you can drop "sometype" to write :
template <typename T>
struct test<T, typename = std::enable_if_t<std::is_integral_v<T>>> {
int t = 42;
};
Finally compare this with the default values used in function declarations:
void foo(int = 9); //Function declaration can have default values without names too.
void foo (int a )
{
//some code
}

Possible to use type_traits / SFINAE to find if a class defines a member TYPE?

I have seen this question which allows one to check for the existence of a member function, but I'm trying to find out whether a class has a member type.
In the example below, both evaluate to "false", but I would like to find a way so that has_bar<foo1>::value evaluates to false, and has_bar<foo2>::value evaluates to true.
Is this possible?
#include <iostream>
struct foo1;
struct foo2 { typedef int bar; };
template <typename T>
class has_bar
{
typedef char yes;
typedef long no;
template <typename C> static yes check( decltype(&C::bar) ) ;
template <typename C> static no check(...);
public:
enum { value = sizeof(check<T>(0)) == sizeof(yes) };
};
int main()
{
std::cout << has_bar<foo1>::value << std::endl;
std::cout << has_bar<foo2>::value << std::endl;
return 0;
}
Edit: implementing a specialisation in response to the answers below:
...if you use C::bar in the target template, the template will be
discarded automatically for types that don't have that nested type.
I have tried to do this, but am clearly missing something
#include <iostream>
struct foo1;
struct foo2 { typedef int bar; };
template <typename T, typename U = void>
struct target
{
target()
{
std::cout << "default target" << std::endl;
}
};
template<typename T>
struct target<T, typename T::bar>
{
target()
{
std::cout << "specialized target" << std::endl;
}
};
int main()
{
target<foo1>();
target<foo2>();
return 0;
}
Try this
template<class T>
struct Void {
typedef void type;
};
template<class T, class U = void>
struct has_bar {
enum { value = 0 };
};
template<class T>
struct has_bar<T, typename Void<typename T::bar>::type > {
enum { value = 1 };
};
You cannot obtain a pointer to member to a type member:
template <typename C> static yes check( decltype(&C::bar) ) ;
The subexpression &C::bar will only be valid when bar is a non-type member of C. But what you need to check is whether it is a type. A minimal change to your template could be:
template <typename C> static yes check( typename C::bar* ) ;
If bar is a nested type of C, then that function overload will be a valid candidate (the 0 will be a pointer to whatever C::bar type is), but if C does not contain a nested bar then it will be discarded and the second test will be the only candidate.
There is a different question as of whether the trait is needed at all, since if you use C::bar in the target template, the template will be discarded automatically for types that don't have that nested type.
EDIT
What I meant is that in your approach you need to create a trait for each and every possible nested type, just to generate a template that does or does not hold a nested type (enable_if). Let's take a different approach... First we define a general utility to select a type based on a condition, this is not required for this problem, and a simpler template <typename T> void_type { typedef void type; }; would suffice, but the utility template can be useful in other cases:
// General utility: if_<Condition, Then, Else>::type
// Selects 'Then' or 'Else' type based on the value of
// the 'Condition'
template <bool Condition, typename Then, typename Else = void>
struct if_ {
typedef Then type;
};
template <typename Then, typename Else>
struct if_<false, Then, Else > {
typedef Else type;
};
Now se just need to use SFINAE for class template specializations:
template <typename T, typename _ = void>
struct target {
// generic implementation
};
template <typename T>
struct target<T, typename if_<false,typename T::bar>::type> {
// specialization for types holding a nested type `T::bar`
};
Note that the main difference with your approach is the use of an extra intermediate template (the one for which Substitution will Fail --and Is Not An Error) that yields a void type (on success). This is the reason why the void_type template above would also work: you just need to use the nested type as argument to a template, and have that fail, you don't really care what the template does, as long as the evaluation is a nested type (that must be void) if it succeeds.
In case it is not obvious (it wasn't at first for me) why your approach doesn't work, consider what the compiler needs to do when it encounters target<foo2>: The first step is finding that there is a template called target, but that template takes two arguments of which only one was provided. It then looks in the base template (the one that is not specialized) and finds that the second argument can be defaulted to void. From this point on, it will consider your instantiation to be: target<foo2,void> (after injecting the defaulted argument). And it will try to match the best specialization. Only specializations for which the second argument is void will be considered. Your template above will only be able to use the specialized version if T::bar is void (you can test that by changing foo2 to: struct foo2 { typedef void bar; }. Because you don't want the specialization to kick in only when the nested type is void you need the extra template that will take C::bar (and thus fail if the type does not contain a nested bar) but will always yield void as the nested type.
C++20 Update:
It is now much more easier to check whether a given type contains a specific type definition.
template<typename T>
concept has_bar = requires {
typename T::bar;
};
... so your example code evolves to this:
#include <iostream>
struct foo1;
struct foo2 { typedef int bar; };
template <typename T, typename U = void>
struct target
{
target()
{
std::cout << "default target" << std::endl;
}
};
template<typename T>
requires(has_bar<T>)
struct target<T>
{
target()
{
std::cout << "specialized target" << std::endl;
}
};
int main()
{
target<foo1>();
target<foo2>();
return 0;
}
Example on gcc.godbolt: https://gcc.godbolt.org/z/a15G13
I prefer to wrap it in macro.
test.h:
#include <type_traits>
template<typename ...>
struct void_type
{
using type = void;
};
template<typename ...T>
using void_t = typename void_type<T...>::type;
#define HAS_TYPE(NAME) \
template<typename, typename = void> \
struct has_type_##NAME: std::false_type \
{}; \
template<typename T> \
struct has_type_##NAME<T, void_t<typename T::NAME>>: std::true_type \
{} \
HAS_TYPE(bar);
test.cpp:
#include <iostream>
struct foo1;
struct foo2 { typedef int bar; };
int main()
{
std::cout << has_type_bar<foo1>::value << std::endl;
std::cout << has_type_bar<foo2>::value << std::endl;
return 0;
}

Variadic templates - incomplete type

Having this code:
template<class ...Args>
struct Are_Same
{
enum {value = Are_Same<Args...>::value};
};
template<class A,class... C>
struct Are_Same<A,C...>
{
enum {value = Are_Same<A,C...>::value};//HERE is THE ERROREOUS LINE
};
template<class A,class B>
struct Are_Same<A,B>
{
enum {value = std::is_same<A,B>::value};
};
I'm getting error from gcc 4.6.1:
error: incomplete type 'Are_Same' used in
nested name specifier.
I thought that by doing Are_Same<A,C...>::value I will invoke recursive call which at the end will simply expand to Are_Same<A,B>. Obviously it's not the case. Anyone knows where am I making mistake?
I think that the definitions of the templates are wrong, in both cases you are triggering exact recursion. I would have expected the compiler to die with some stackoverflow inside the compiler but a different error is produced...
An implementation of the are_same variadic template could be:
template <class... Args> // base (optional to declare the template)
struct are_same;
template <class A, class B, class... Args> // recursion
struct are_same<A,B,Args...> {
static const bool value = is_same<A,B>::value && are_same<A,Args...>::value;
};
template <class A, class B> // stop condition
struct are_same<A,B> {
static const bool value = is_same<A,B>::value;
};
Note that in the recursion step, one argument is dropped from the list of arguments, so that the new problem to resolve is a reduced version of the original. This type of template metaprogramming is quite related to recursion, and the same rules apply, to be able to use recursion you need to ensure that each recursive step gets you closer to a solution. In this particular case, given a list of N potentially same types, each step reduces the problem to finding whether N-1 types are the same.
You can use alternatively, as stop condition (replacing the former one) a degenerate version of the are_same problem:
template <class A>
struct are_same<A> {
static const bool value = true;
};
Which is degenerate in the sense that it does not really make sense asking whether a single type *are_same*, but for different metaprogramming tasks it could be appropriate.
A different potentially more efficient algorithm (I am not sure whether the compiler will avoid the instantiation of the template in the recursion step above) that does not depend on is_same could be:
template <class... Args>
struct are_same;
template <class A, class... Args>
struct are_same<A,A,Args...> { // recursion
static const bool value = are_same<A,Args...>::value;
};
template <class A, class B, class... Args>
struct are_same<A,B,Args...> { // cut, A and B are not the same
static const bool value = false;
};
template <class A>
struct are_same<A> { // end of recursion
static const bool value = true;
};
In this case, the compiler will prefer the recursion to the cut steps whenever the two types are the same, so we need not check is_same internally. At the same time, if the compiler goes into the cut step, we don't need to process the rest of the type list, as we already know the answer.
I would do it like this :
#include <type_traits>
#include <iostream>
template <class... Args>
struct are_same
{
static const bool value=true;
};
template <class A, class B, class... Args> // recursion
struct are_same<A,B,Args...> {
static const bool value = std::is_same<A,B>::value && are_same<B,Args...>::value;
};
int main()
{
std::cout<< std::boolalpha << are_same< int >::value << std::endl;
std::cout<< std::boolalpha << are_same< int, int, int >::value << std::endl;
std::cout<< std::boolalpha << are_same< int, int, double, int >::value << std::endl;
}
Probably simplest implementation could be like this:
template <typename... TList>
struct are_same { constexpr static bool value = false; };
template <typename T, typename... TList>
struct are_same<T, T, TList...> {
constexpr static bool value = are_same<T, TList...>::value;
};
template <typename T>
struct are_same<T> { constexpr static bool value = true; };
Alternatively you can replace stop condition with
template <typename T>
struct are_same<T, T> { constexpr static bool value = true; };
But the first one is more general because are_same<type>::value == true. Another question is what should be are_same<>::value equal to. This gives you false but it is not a big deal to add one more template specialisation like this.
template <>
struct are_same<> { constexpr static bool value = true; };