Understanding Alias Templates - c++

I asked a question that has several references to the code:
template <typename...>
using void_t = void;
I believe I have a generally misunderstand alias templates:
Why wouldn't you just evaluate whatever template parameter you're passing into an alias template in an enable_if_t or conditional_t statement?
Is the code above just about doing an enable_if_t on multiple template parameters at once?
Secondly, I believe that I have a specific misunderstanding of the role of void_t. This comment states that the C++17 standard defines void_t. Here's what I don't get:
Isn't void_t just an arbitrary name? If I still have to define template <typename...> using void_t = void; wherever I plan to use void_t what's the point of standardizing an arbitrary name?

In Barry's example from your linked question:
template<typename T, typename = void>
struct has_to_string
: std::false_type { };
template<typename T>
struct has_to_string<T,
void_t<decltype(std::to_string(std::declval<T>()))>
>
: std::true_type { };
void_t is just used to translate the type deduced by decltype to void so that it matches the default argument to the primary template definition. The SFINAE is all taken care of by the decltype expression. You could just as easily do the following:
//use , void() instead of wrapping in void_t
//this uses the comma operator to check the type of the to_string call, then change the type to void
decltype(std::to_string(std::declval<T>()), void())
The former version is much easier to read and void_t doesn't require decltype to work.
If void_t is available in your implementation you don't need to redefine it. When it's standardised it will be available just like any of the other alias templates in the standard.
Think about it this way: if T is int, which has a valid std::to_string overload, deduction will look like this:
has_to_string<int> -> has_to_string<int,void> because of the default argument. So lets look for specializations of has_to_string with those arguments.
template<typename T>
struct has_to_string<T,
void_t<decltype(std::to_string(std::declval<T>()))>
>
: std::true_type { };
Okay, that is a partial specialization for some T and some dependent type. Let's work out that type:
void_t<decltype(std::to_string(std::declval<T>()))>
//std::to_string(int&&) is valid and returns a std::string
void_t<std::string>
//void_t changes types to void
void
Now our specialization looks like this:
template<>
struct has_to_string<int,void>
: std::true_type { };
This matches our instantiation of has_string<int,void>, so has_to_string<int> inherits from std::true_type.
Now think about it when T is struct Foo{};. Again, let's work out that dependent type:
void_t<decltype(std::to_string(std::declval<T>()))>
//wait, std::to_string(Foo&&) doesn't exist
//discard that specialization
With that specialization discarded, we fall back to the primary template:
template<typename T, typename = void>
struct has_to_string
: std::false_type { };
So has_to_string<Foo> inherits from std::false_type.

I don't think the shown example really shows what void_t is good for as it only shows one use case, but when you look at
template<typename T>
struct has_to_string<T,
void_t<decltype(std::to_string(std::declval<T>()))>
>
: std::true_type { };
it is not so much different from
template<typename T>
struct has_to_string<T,
decltype(std::to_string(std::declval<T>()), void())
>
: std::true_type { };
And for this statement:
The former version is much easier to read and void_t doesn't require decltype to work.
I think the advantage in readability is quite small and the second part makes no sense, when decltype doesn't work, SFINAE kicks in as expected.
One example where void_t is more useful is the one from the proposal:
// primary template handles types that have no nested ::type member
template< class, class = void_t<> >
struct has_type_member
: std::false_type { };
// specialization recognizes types that do have a nested ::type member
template< class T >
struct has_type_member<T, void_t<typename T::type>>
: std::true_type { }
As you can see, even the primary template uses void_t to increase the readability as it now matches the specialization. That is not strictly necessary, but I like it. The real power comes when you think about the alternatives. Without void_t, the specialization is now more complicated:
template< class T >
struct has_type_member<T, decltype(typename T::type, void())>
: std::true_type { }
wouldn't work as T::type names a type, not an expression. You therefore need
template< class T >
struct has_type_member<T, decltype(std::declval<typename T::type>(), void())>
: std::true_type { }
The whole expression becomes longer, more tricky and it might suffer from edge-cases you forgot to handle. This is where void_t really helps, the other uses are then just a small improvement and they increase consistency.

Related

How to detect a noexcept method using SFINAE

I'm asking about a variation of a (popular) question - detecting the existence of a method of a class.
I've read many answers here in SO, and most of the (post C++17) solutions look like this:
#include <type_traits>
template<class ...Ts>
struct voider{
using type = void;
};
template<class T, class = void>
struct has_foo : std::false_type{};
template<class T>
struct has_foo<T, typename voider<decltype(std::declval<T>().foo())>::type> : std::true_type{};
Basically, we're letting the compiler decide using a "trick" :
if the expression std::declval<T>().foo() is well-formed,
then decltype(std::declval<T>().foo()) doesn't produce a compiler-error,
then the compiler "prefers" has_foo<T, typename voider<decltype(...)>>::type since it doesn't need to replace the second template type with a default type.
great, but how can we combine noexcept with it?
I've tried many ways but it seems most techniques (including decltype(declval<type>.my_func()))
only care about the name, return type and the argument types and not about the noexcept.
You can do it with the help of noexpect operator (since C++11).
The noexcept operator performs a compile-time check that returns true if an expression is declared to not throw any exceptions.
e.g.
template<class T>
struct has_foo<T,
typename voider<decltype(std::declval<T>().foo()),
std::enable_if_t<noexcept(std::declval<T>().foo())>
>::type
> : std::true_type{};
LIVE

How to simplify enable_if alias in template template parameter

My goal is to have a struct that takes in an alias to a specialized enable_if_t<> along with a typename variadic parameter pack and then tells me whether the enable_if's conditions were satisfied for all of the types in the pack. I have a bunch of these specialized enable_ifs, but need to write tests for them before we can put them into our open source project. I have about 2000+ lines of code manually testing these specializations, but bet I can get it to 100 or 200 if I can figure out the pattern below. I have a working version (+ godbolt link), but tbh I'm not sure why it's working and that scheme is breaking in a case where the implementation receives a parameter pack
Here is an example of the code I would like to write and it's result. I'm using C++14 and can steal basic implementations of things from C++17 likes conjunction and void_t
#include <type_traits>
#include <string>
// enable_if for arithmetic types
template <typename T>
using require_arithmetic = typename std::enable_if_t<std::is_arithmetic<T>::value>;
const bool true_arithmetic = require_tester<require_arithmetic, double, int, float>::value;
// output: true
// If any of the types fail the enable_if the result is false
const bool false_arithmetic = require_tester<require_arithmetic, double, std::string, float>::value;
// output: false
The below does do what I want, but tbf I'm not really understanding how.
// Base impl
template <template <class> class Check, typename T1, typename = void>
struct require_tester_impl : std::false_type {};
// I'm not totally sure why void_t needs to be here?
template <template <class> class Check, typename T1>
struct require_tester_impl<Check, T1, void_t<Check<T1>>> : std::true_type {};
// The recursive version (stolen conjuction from C++17)
template <template <class> class Check, typename T = void, typename... Types>
struct require_tester {
static const bool value = conjunction<require_tester_impl<Check, T>,
require_tester<Check, Types...>>::value;
};
// For the end
template <template <class> class Check>
struct require_tester<Check, void> : std::true_type {} ;
In particular, I'm not sure why the void_t is needed in the impl partial specialization for std::true_type.
What I would like to get to is a require_variadic_tester that takes in a variadic templated alias, something like enable_if<conjunction<check<T...>>::value>, and gives me true or false. Sadly, the below returns false no matter what types come in
// impl
template <template <class...> class Check, typename... Types>
struct require_variadic_impl : std::false_type {};
// Adding void_t here causes the compiler to not understand the partial specialiation
template <template <class...> class Check, typename... Types>
struct require_variadic_impl<Check, Check<Types...>> : std::true_type {};
template <template <class...> class Check, typename... Types>
struct require_variadic_tester : require_variadic_impl<Check, Types...> {};
I would like the following given the input, but can't seem to shake how to hide that conjunction one level lower
// Enable if for checking if all types are arithmetic
template <typename... Types>
using require_all_arithmetic = std::enable_if_t<conjunction<std::is_arithmetic<Types>...>::value>;
require_variadic_tester<require_all_arithmetic, double, double, double>::value;
// is true
require_variadic_tester<require_all_arithmetic, double, std::string, double>::value;
// is false
I think my failure to understand void_t in the first meta function is causing my misunderstanding
Below is the godbolt, any help in understanding this is very appreciated!
https://godbolt.org/z/8XNqpo
Edit:
To give more context in why I want the above with the conjunction inside of the enable_if_t. I'm stuck on C++14 but we are adding a new feature to our open source math library which without more generic types (and requirements on those generic types) we will end up with a ton of code bloat. We currently have stuff like this
template <int R, int C>
inline Eigen::Matrix<double, R, C> add(
const Eigen::Matrix<double, R, C>& m1, const Eigen::Matrix<double, R, C>& m2) {
return m1 + m2;
}
I'd like to have more generic templates and do something like this
template <typename Mat1, typename Mat2,
require_all_eigen<is_arithmetic, Mat1, Mat2>...>
inline auto add(Mat1&& m1, Mat2&& m2) {
return m1 + m2;
}
I have all of those require_*_<container> aliases setup, but the tests for all of those requires is about 2000+ lines and in the future that will be a funky mess to have to deal with.
We have unary and variadic template enable_if aliases, at this point the above unary case does what I want ala a nice test like
#include <gtest/gtest.h>
TEST(requires, arithmetic_test) {
EXPECT_FALSE((require_tester<require_arithmetic, std::string>::value));
EXPECT_TRUE((require_tester<require_arithmetic, double, int, float>::value));
}
The issue I have is with testing the variadic template enable_if aliases, where I want to be able to write something like
// Enable if for checking if all types are arithmetic
template <typename... Types>
using require_all_arithmetic = std::enable_if_t<conjunction<std::is_arithmetic<Types>...>::value>;
/// For the tests
TEST(requires, arithmetic_all_test) {
EXPECT_FALSE((require_variadic_tester<require_all_arithmetic, std::string,
Eigen::Matrix<float, -1, -1>>::value));
EXPECT_TRUE((require_variadic_tester<require_all_arithmetic,
double, int, float>::value));
}
If I can test all of this I think the requires part of our library alone could be a nice header only mini-library for what I'm calling "bad fake concepts in 14" (or bfc14 for short ;-))
Here's what happens with your require_tester<require_arithmetic, double, double, int>:
This doesn't match the partial specialization of require_tester, which has just two template arguments <Check, void>, so we use the primary template
template <template <class> class Check, typename T, typename... Types>
struct require_tester;
with Check = require_arithmetic; T = double; Types = double, int. It does not match the partial specialization of require_tester. Member value is the result of
conjunction<require_tester_impl<Check, T>, require_tester<Check, Types...>>::value
where the interesting part is require_tester_impl<Check, T> = require_tester_impl<require_arithmetic, double>. First, since the template parameters of require_tester_impl are
template <template <class> class Check, typename T1, typename = void>
and only two explicit template argumetns are given, we know the actual template arguments are <require_arithmetic, double, void>. Now we need to see whether or not this matches the partial specialization of require_template_impl, so we try to match:
require_template_impl<require_arithmetic, double, void>
require_template_impl<Check, T1, void_t<Check<T1>>>
So template argument deduction finds Check = require_arithmetic and T1 = double. The type void_t<Check<T1>> does not cause any deduction of Check or T1. But the deduced parameter values must be substituted in, and we find void_t<Check<T1>> is void_t<require_arithmetic<double>> is void. This does match the void from the template arguments, so the partial specialization does match, and require_template_impl<require_arithmetic, double, void> inherits std::true_type, not std::false_type.
On the other hand, if T1 were std::string instead of double, substituting the deduced template arguments in would find void_t<require_arithmetic<std::string>> is invalid, via the eventual enable_if<...>::type where no member type exists. When substituting deduced template arguments into other template parameters fails, this means the partial specialization is thrown out as not a match. So require_template_impl<require_arithmetic, std::string, void> uses the primary template and inherits std::false_type.
Going back to the value member of require_tester, it recursively finds require_tester<require_arithmetic, double, int>::value via require_tester<require_arithmetic, int>::value via require_tester<require_arithmetic>::value which is the same as require_tester<require_arithmetic, void>::value. All the value members are true, so the final value is true.
Though I would simplify this a bit:
The void is unnecessary in the require_tester recursion, and causes the strange "fact" that require_tester<Anything, void>::value is always true. It would be better to remove the = void default from the primary require_tester template, and make the base case template <template <class> class Check> require_tester<Check> instead.
Your value expression in the require_tester primary template is always giving exactly two template arguments to conjunction, so it's not really using its variadic property, and you could just as well write require_tester_impl<...>::value && require_tester<...>::value. Since require_tester is doing a recursion itself, it doesn't need the recursive definition abstracted into conjunction. Instead, require_tester could be simplified to count on conjunction and avoid doing any recursion itself:
template <template <class> class Check, typename... Types>
struct require_tester : conjunction<require_tester_impl<Check, Types>...>
{};
// No other specialization needed.
The require_variadic_tester template can follow a similar pattern, except that I'll give the dummy template parameter which was just typename = void a name, typename Enable. And it needs to come before the template parameter pack, so it's not that useful to actually default it to void, and we need to make sure to use the appropriate void template argument in the corresponding position.
template <template <class...> class Check, typename Enable, typename... Types>
struct require_variadic_impl : std::false_type {};
template <template <class...> class Check, typename... Types>
struct require_variadic_impl<Check, void_t<Check<Types...>>, Types...> : std::true_type {};
template <template <class...> class Check, typename... Types>
struct require_variadic_tester : require_variadic_impl<Check, void, Types...> {};
See the modified program on godbolt, with desired results.
Not sure to understand all your needs but...
What I would like to get to is a require_variadic_tester that takes in a variadic templated alias, something like enable_if<conjunction<check<T...>>::value>, and gives me true or false. Sadly, the below returns false no matter what types come in
Are you sure that you want conjunction<check<T...>> ?
Or do you want conjunction<check<T>...>?
I mean... the check must receive a variadic list of types or do you want to check a an alias, that (as in your example) receive a single type and a conjunction that is true iff (if and only if) the check is satisfied for all types?
In this second case, std::void_t is very handy to verify that all checks are satisfied.
I propose the following require_variadic_impl and require_variadic_tester
template <template <typename> class, typename, typename = void>
struct require_variadic_impl
: public std::false_type
{ };
template <template <typename> class C, typename ... Ts>
struct require_variadic_impl<C, std::tuple<Ts...>, std::void_t<C<Ts>...>>
: public std::true_type
{ };
template <template <typename> class C, typename ... Ts>
struct require_variadic_tester
: public require_variadic_impl<C, std::tuple<Ts...>>
{ };
Now from
template <typename T>
using require_arithmetic = typename std::enable_if_t<std::is_arithmetic<T>::value>;
// ...
printf("\nGeneric Variadic: \n\n");
const char* string_generic_var_check =
require_variadic_tester<require_arithmetic, std::string>::value ? "true" : "false";
const char* double_generic_var_check =
require_variadic_tester<require_arithmetic, double, double, double>::value ? "true" : "false";
std::printf("\t String: %s\n", string_generic_var_check);
std::printf("\t Double: %s\n", double_generic_var_check);
you get
Generic Variadic:
String: false
Double: true
think my failure to understand void_t in the first meta function is causing my misunderstanding
Try thinking std::void_t<Ts...> as "enable if all Ts are enabled".
template <template <class> class Check, typename T1, typename = void>
struct require_tester_impl : std::false_type {};
// I'm not totally sure why void_t needs to be here?
template <template <class> class Check, typename T1>
struct require_tester_impl<Check, T1, void_t<Check<T1>>> : std::true_type {};
Here, you required the third parameter of require_tester_impl is of type void, since you wrote it as default value. If the user, when specializing require_tester_impl doesn't specify its third parameter, it is void. So the compiler will search for a partial specialization where the first template parameter is a unary class template, the second template parameter is a type, and the third one is void, otherwise, no partial specialization will be found, since the third parameter of any partial specialization will fail.
That's where void_t comes into play. Since you want to inject Check into the parameter, but you require void, that's when void_t comes handy, since every type used to specialized it is mapped to void, which is what you really need. When the partial specialization doesn't fail, you will have two enabled specializations, the default one, and the partial one.
The partial one will be finally choosen since it is more specialized than the other, since void have been calculated in a way dependant on other template parameters.
That's for the first part. For the second part (the variadic template), remember that, if enable_if succeds, it returns void.
So your require_variadic_impl:
template <template <class...> class Check, typename... Types>
struct require_variadic_impl : std::false_type {};
// Adding void_t here causes the compiler to not understand the partial specialiation
template <template <class...> class Check, typename... Types>
struct require_variadic_impl<Check, Check<Types...>> : std::true_type {};
have a problem here, and it's that, Check<Types...>, since it is aliased to enable_if, returns void when it success, however, the second parameter of require_variadic_impl is not void so the partial specializations finally fails when the check is correct. When it isn't, then the enable_if have no inner type defined,the partial specialization also fails, and the base case is used again.
However, do it simple. I propose here a much more readable implementation with same final result:
#include <iostream>
#include <type_traits>
#include <string>
template<class... Ts>
struct require_all_arithmetic : std::conjunction<std::is_arithmetic<Ts>...>
{};
template<template<class...> class Check, class... Ts>
struct require_variadic_tester : Check<Ts...>
{};
int main()
{
std::cout << require_variadic_tester<require_all_arithmetic, double, double, double>::value << std::endl;
std::cout << require_variadic_tester<require_all_arithmetic, double, std::string, double>::value << std::endl;
}
https://coliru.stacked-crooked.com/a/f9fb68e04eb0ad40
Or just:
#include <iostream>
#include <type_traits>
#include <string>
template<class... Ts>
struct require_all_arithmetic : std::conjunction<std::is_arithmetic<Ts>...>
{};
int main()
{
std::cout << require_all_arithmetic<double, double, double>::value << std::endl;
std::cout << require_all_arithmetic<double, std::string, double>::value << std::endl;
}
However, if you require a check that is sfinae-friendly, plus a struct that maps "sfinae"-friendly checks to true/false, you can use constexpr methods instead. It's much more simple:
template<class... Ts>
using require_all_arithmetic = std::enable_if_t<std::conjunction<std::is_arithmetic<Ts>...>::value>;
template<template<class...> class Check, class... Ts, class = Check<Ts...> >
constexpr bool require_variadic_tester_impl(int)
{ return true; }
template<template<class...> class Check, class... Ts>
constexpr bool require_variadic_tester_impl(unsigned)
{ return false; }
template<template<class...> class Check, class... Ts>
struct require_variadic_tester
{ static constexpr bool value = require_variadic_tester_impl<Check, Ts...>(42); };
int main()
{
std::cout << require_variadic_tester<require_all_arithmetic, double, double, double>::value << std::endl;
std::cout << require_variadic_tester<require_all_arithmetic, double, std::string, double>::value << std::endl;
}
The technique works as follow: if Check fails, only the second overload will compile, which returns false. However, if the check is valid and the inner enable_if is defined, then both overloads will be valid but, since you have passed an int (42), and the second overload receives an unsigned, the first overload will be a better match, returning true.
https://coliru.stacked-crooked.com/a/bfe22ea099dd5749
Finally, if you want the check always is true_type or false_type, then, instead of inheriting, you can just alias std::conditional:
template<template<class...> class Check, class... Ts>
using require_variadic_tester =
std::conditional_t<require_variadic_tester_impl<Check, Ts...>(42),
std::true_type, std::false_type>;

Can I pattern-match a type without writing a custom trait class?

Since C++20 concepts aren't standardized yet, I'm using static_assert as a makeshift concept check, to provide helpful error messages if a type requirement isn't met. In this particular case, I have a function which requires that a type is callable before getting its result type:
template <typename F, typename... Args>
void example() {
static_assert(std::is_invocable_v<F, Args...>, "Function must be callable");
using R = std::invoke_result_t<F, Args...>;
// ...
}
In addition, I require that the callable's result must be some kind of std::optional, but I don't know what type the optional will hold, so I need to get that type from it:
using R = // ...
using T = typename R::value_type; // std::optional defines a value_type
However, this will fail if type R doesn't have a value_type, e.g. if it's not a std::optional as expected. I'd like to have a static_assert to check for that first, with another nice error message if the assertion fails.
I could check for an exact type with something like std::is_same_v, but in this case I don't know the exact type. I want to check that R is some instance of std::optional, without specifying which instance it must be.
One way to do that is with a helper trait:
template <typename T>
struct is_optional { static constexpr bool value = false; };
template <typename T>
struct is_optional<std::optional<T>> { static constexpr bool value = true; };
template <typename T>
constexpr bool is_optional_v = is_optional<T>::value;
…and then I can write:
static_assert(is_optional_v<R>, "Function's result must be an optional");
That works, but it seems a little awkward to pollute my namespace with a helper trait just for a one-off check like this. I don't expect to need is_optional anywhere else, though I can imagine possibly ending up with other one-off traits like is_variant or is_pair too.
So I'm wondering: is there a more concise way to do this? Can I do the pattern matching on instances of std::optional without having to define the is_optional trait and its partial specialization?
Following the suggestion by several respondents, I made a re-usable trait:
template <typename T, template <typename...> typename Tpl>
struct is_template_instance : std::false_type { };
template <template <typename...> typename Tpl, typename... Args>
struct is_template_instance<Tpl<Args...>, Tpl> : std::true_type { };
template <typename T, template <typename...> typename Tpl>
constexpr bool is_template_instance_v = is_template_instance<T, Tpl>::value;
…so that I can write:
static_assert(is_template_instance_v<R, std::optional>, "Function's result must be an optional");
This is just as many lines and declarations as the is_optional trait, but it's no longer a one-off; I can use the same trait for checking other kinds of templates (like variants and pairs). So now it feels like a useful addition to my project instead of a kluge.
Can I do the pattern matching on instances of std::optional without having to define the is_optional trait and its partial specialization?
Maybe using implicit deduction guides for std::optional?
I mean... something as
using S = decltype(std::optional{std::declval<R>()});
static_assert( std::is_same_v<R, S>, "R isn't a std::optional" );
Explanation.
When R is std::optional<T> for some T type, std::optional{r} (for an r value of type R) should call the copy constructor and the resulting value should be of the same type R.
Otherwise, the type should be different (std::optional<R>).
The following is a full compiling example.
#include <iostream>
#include <optional>
template <typename T>
bool isOptional ()
{
using U = decltype(std::optional{std::declval<T>()});
return std::is_same_v<T, U>;
}
int main ()
{
std::cout << isOptional<int>() << std::endl; // print 0
std::cout << isOptional<std::optional<int>>() << std::endl; // print 1
}
Anyway, I support the suggestion by super: create a more generic type-traits that receive std::option as template-template argument.

Simplest implementation of "lightweight type categorization idiom"?

My goal is to implement a predicate that detects the presence of a nested using alias (or typedef) that acts as a light-weight tag to indicate that a class has some attribute (for the purposes of generic programming). For example, a has_my_tag<T> predicate should behave as follows:
struct A {
using my_tag = void;
};
struct B {};
int main()
{
static_assert(has_my_tag<A>::value, ""); // evaluate to true if my_tag=void is present
static_assert(!has_my_tag<B>::value, ""); // false otherwise
}
User #JoelFalcou called this the "lightweight type categorization idiom" and provided a solution in this answer. I have been unable to find any references for an idiom of that name (do you know of any?) Here's Joel's implementation of has_my_tag<>:
template<class T, class R = void>
struct enable_if_type { typedef R type; };
template<class T, class Enable = void>
struct has_my_tag : std::false_type {};
template<class T>
struct has_my_tag<T, typename enable_if_type<typename T::my_tag>::type> :
std::true_type
{};
And here is a working version on the Compiler Explorer: https://godbolt.org/z/EEOBb-
I have come up with the following simplified version:
template<class T, class Enable = void>
struct has_my_tag : std::false_type {};
template<class T>
struct has_my_tag<T, typename T::my_tag> : std::true_type
{};
https://godbolt.org/z/yhkHp7
My Questions: Is the simplified version an acceptable way to implement the idiom? Are there circumstances where it would fail? Is there a simpler version that works in C++11? Which version should I prefer?
From what I understand, Joel's version would allow my_tag to alias any type, whereas my version requires my_tag to alias void. But given the goal of tagging types for light-weight predicate testing, I am not clear which version is to be preferred.
Auxiliary questions: Also, are there other names for this idiom? Is it used in any libraries that I could investigate? So far I have not found a name that brings up any search results.
For your setup, there is no difference between the original version and yours. Both use SFINAE to select the correct has_my_tag. Your version does however constrain your typedef/using to be my_tag=void. if my_tag is typedef'd as any other type, your specialization will not match, and you will end up instantiating the primary template as can be seen here.
The reason for this is that where you instanciate the templates in main, static_assert(has_my_tag<A>::value, ""); you are not specifying the second parameter, so the default (void) is used, that is has_my_tag<A,void>::value
Your specialization must match this to be considered.
The usage of enable_if_type, (basically doing the job of void_t in c++17) is to enable SFINAE on the ::type member of T, but then always result in void, such that your specialization will match when ::type exists, regardless of the type of the my_tag typedef.
This allows you to just worry about whether it exists, and not its type;
Personally I would use the approach that doesn't depend on my_type being typedef'd as void, either the enable_if_type version, or something like...
#include <iostream>
#include <type_traits>
struct A {
using my_tag = void;
};
struct B {};
struct C {
using my_tag = int; // with void_t also works with my_tag = int
};
struct D {
struct my_tag{}; //or some struct as the tag
};
// same as your enable_if_type
template <typename...>
using void_t = void;
template<class T, class Enable = void>
struct has_my_tag : std::false_type {};
template<class T>
struct has_my_tag<T, void_t<typename T::my_tag>> : std::true_type
{};
int main() {
std::cout << has_my_tag<A>::value << std::endl;
std::cout << has_my_tag<B>::value << std::endl;
std::cout << has_my_tag<C>::value << std::endl;
std::cout << has_my_tag<D>::value << std::endl;
return 0;
}
Demo
First, yours works, but does depend on it being void. That lightweight tag might be useful if it carried a non-void type in some situations, and if it is non-void you silently get detection failing, which seems bad.
Second, your type tagging require you to modify the type, which means you cannot get built-in or types you don't own (like ones in std). We can fix this.
namespace type_tag {
namespace adl {
template<template<class...>class tag>
struct tag_token_t {};
template<class T, template<class...> class tag>
constexpr
decltype( (void)(std::declval<tag<T>>()), std::true_type{} )
tag_test( T*, tag_token_t<tag> ) { return {}; }
template<class T, template<class...> class tag, class...LowPriority>
constexpr
std::enable_if_t<!std::is_same<T,int>{}, std::false_type> tag_test(T*, tag_token_t<tag>, LowPriority&&...) {
return {};
}
}
template<template<class...>class Z>using tag_token_t = adl::tag_token_t<Z>;
template<template<class...>class tag>
constexpr tag_token_t<tag> tag_token{};
namespace details {
template<class T, template<class...>class tag>
constexpr auto test_impl( T*, tag_token_t<tag> ) {
return tag_test( (T*)nullptr, tag_token<tag> );
}
}
template<class T, template<class>class tag>
constexpr auto tag_test() {
return details::test_impl((T*)nullptr, tag_token<tag>);
}
}
so now a tag is this:
template<class T>
using my_tag = typename T::my_tag;
we can test it as follows:
constexpr auto double_has_tag = type_tag::tag_test< double, my_tag >();
which returns a compile-time true or false if double has a tag.
We can decide that int has a tag by doing:
namespace type_tag::adl {
constexpr std::true_type tag_test( int*, type_tag::tag_token_t<my_tag> ) {
return {};
}
}
or, for types that we control:
struct my_tagged_type {
using my_tag = void;
};
for types we can inject names into their namespace (ie, not std or built-in types) we can do:
namespace not_my_ns {
constexpr std::true_type tag_test( not_my_type*, ::tag_test::tag_token_t<::my_tag> ) {
return {};
}
}
and suddenly type_tag::tag_test<not_my_ns::not_my_type, ::my_tag>() is truthy.
Once we have tag_test< type, tag_name >() we can use the usual std::enable_if instead of some custom system.
The advantages of this system include:
It can be extended without changing anything about the type you are tagging.
It can be extended using the using tag=void; or using tag=int; that your system works with.
At SFINAE point of use, it is just another compile-time bool. So your existing SFINAE patterns work with it.
If you pick a poor name for the tag type in the struct that someone else is using for an unrelated reason, tag_test can override this on a per-type basis.
The disadvantage is that it took a bit of magic to do this. But in common use cases, you get the same work required for end-users as your lightweight system. In more complex use cases, this lets you do things your lightweight one won't.
Live example.

std::void_t and nested non type members

I have the following code where I get unexpected result (second static_assert fails):
#include <type_traits>
template <typename T, typename = void>
struct is_bananas : std::false_type {};
template <typename T>
struct is_bananas<T, std::void_t<typename T::config::num_items>>
: std::true_type {};
struct Config{
static constexpr int num_items=42;
};
struct Bananas{
using config = Config;
};
static_assert(is_bananas<int>::value == false);
static_assert(is_bananas<Bananas>::value == true);
When I use T::config instead of T::config::num_items code works as expected.
If I use decltype around num_items then it works.
Am I correct to assume that void_t only works with types?
Is there a nicer way to do what I want?
If somebody is confused by this(and think: just throw a decltype) - I find decltype hard to read in real code when there are long names and deep nesting so if there is a nicer way to do this with void_t or any other template code I would like to know.
Is there a nicer way to do what I want?
Nice and nicer are subjective.
By example, I find very nice your decltype() solution.
Anyway... a possible alternative is define a substitute for std::void_t that works with value types
Something as
template <auto...>
using value_void_t = void;
and write
template <typename T>
struct is_bananas<T, value_void_t<T::config::num_items>>
: std::true_type {};