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>;
Related
I'm planning to create a variable template that takes (variable) template-template parameter and one typename:
template <template <typename> auto MetaPredicate, typename T>
constexpr bool has_predicate_v_ = requires {
{ MetaPredicate<T> } -> std::convertible_to<bool>;
}
Where the expectations are:
template <typename T>
struct dummy_01 {
inline static constexpr bool value = true;
};
template <typename T>
inline constexpr bool dummy_01_v = dummy_01<T>::value;
std::cout << std::boolalpha << has_predicate_v_<dummy_01_v, int> << '\n'; // true
But that doesn't work. It would be useful if they exist in standard.
Another case is to create a metafunction count_if:
template <typename Type, template <typename> bool Predicate>
struct count_if {
inline static constexpr size_t value = /** ... **/;
};
template <typename Type, template <typename> bool Predicate>
inline constexpr size_t count_if_v = count_if<Type, Predicate>::value;
// ...
count_if_v<std::tuple<int, double, void, size_t, unsigned short>,
std::is_integral_v> // yields to 3
There is also a proposal relating to my question: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2008r0.html
Why are there currently no variable template-template parameters/arguments?
What is the status of the proposal?
Are there any possible alternatives for variable template-template parameters/arguments?
You already linked to the proposal, so you’ve largely answered the question yourself. You might not know about the paper tracker that can answer the “status” question; it says that more motivating examples are sought (which might very well have been delayed by the pandemic), so maybe you should contribute yours!
As for alternatives, the usual one is to key on the trait rather than the helper variable template. Obviously new code might have to wrap a (foundational) variable template with a helper trait class template to take advantage of this, but it works.
To extend #DavisHerring's answer: I think there is little use of it: I don't really see the advantage of using the helper variable template instead of the trait directly. Here is e.g. what I would do for your two examples:
In C++20 I would actually write a concept
template <template <typename> class Predicate, typename T>
concept is_predicate = requires {
std::same_as<decltype(Predicate<T>::value), bool>;
};
that makes sure that a given predicate has a static bool member variable value. Before that you might additionally use std::enable_if instead or not use SFINAE at all.
Example 1:
template <template <typename> class MetaPredicate, typename T>
requires is_predicate<MetaPredicate, T>
constexpr bool has_predicate_v_ = requires {
std::same_as<decltype(MetaPredicate<T>::value), bool>;
};
and then call it with the trait has_predicate_v_<dummy_01, int> instead of the alias.
Try it here
Example 2:
template <template <typename> class Predicate, typename... Ts>
requires (is_predicate<Predicate, Ts> && ...)
struct count_if {
inline static constexpr size_t count = ((Predicate<Ts>::value == true ? 1 : 0) + ...);
};
and again call it with the trait std::is_integral instead: count_if<std::is_integral, int, double>::count.
Try it here
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.
I aim to implement a structure template that can be used to detect either if a template substitution is well formed or will fail. An example of usage is to provide two versions of template functions depending on whether the template parameter is comparable or not.
It can be solved quite easily if one provides structures for each scenario explicitly, e.g. whether there exists an equality operator for the template type, as shown here. But I failed to implement a structure that would accept (almost) arbitrary construct as a template argument.
The "best" approach I have reached so far uses template template argument. It compiles, but it does not fit the case when the argument substitution should be well formed.
#include <iostream>
#include <type_traits>
template <typename T = void, typename...>
using Enable = T;
template <bool Cond, typename T = void>
using Enable_if = typename std::enable_if<Cond, T>::type;
template <typename T, template<typename> class X, typename = void>
struct Is_enabled : std::false_type {};
template <typename T, template<typename> class X>
struct Is_enabled<T, X, Enable<X<T>>> : std::true_type {};
/// An example of construct
template <typename T>
using Equals = decltype(std::declval<T>() == std::declval<T>());
template <typename T>
using Enabled_eq = Enable_if<Is_enabled<T, Equals>::value>;
template <typename T>
using Disabled_eq = Enable_if<!Is_enabled<T, Equals>::value>;
template <typename T>
Enabled_eq<T> foo()
{
std::cerr << "enabled!" << std::endl;
}
template <typename T>
Disabled_eq<T> foo()
{
std::cerr << "disabled!" << std::endl;
}
struct A {};
int main(int /*argc*/, const char* /*argv*/[])
{
foo<int>(); /// should print "enabled!"
foo<A>(); /// should print "disabled!"
return 0;
}
In case of int, it should obviously print "enabled!", and in case of A it should print "disabled!". But it always prints "disabled!", so the specialization of Is_enabled is never done.
Am I somewhat close to a correct solution, or will it be more complicated?
The third template parameter of Is_enabled defaults to void. This is what the compiler will use in the Is_enabled<T, Equals> instantiation. That is, Is_enabled<T, X, Enable<X<T>>> : std::true_type {}; can be used only if Enable<X<T>> evaluates to void. By explicitly passing a template argument X<T> to class template Enable declared as:
template <typename T = void, typename...>
using Enable = T;
you actually create an alias for X<T> itself, and the void type (the default one, needed for the dispatching to work) is not used at all. In your case, X<T> is the result of the decltype specifier. For foo<A>() it does lead to instantiation failure. For foo<int>(), however, you get the result type of integers comparison which is bool. That is, although there is no subsitution failure, the compiler cannot use the class template specialization, because it is specialized for void, not bool.
In order to fix the code, you should rewrite Enable to always result with void:
template <typename...>
using Enable = void;
This is also known as std::void_t.
Here is MCVE (uncompilable) :-
#include <iostream>
#include <type_traits>
//-- library ---
template<class T,template<class>class Slot,class DefaultType>
class GetType{
template <typename C> static Slot<T> check( Slot<T>*);
template <typename> static DefaultType check(...);
public: using type=decltype(check<T>());
};
template<class T,template<class>class Slot,class DefaultType>
using X = typename GetType<T,Slot,DefaultType>::type;
Here is its usage :-
//--- user defined ---
class B {public: using MyType=int;};
class C{};
template<class T> using SlotCustom = typename T::MyType;
int main(){
using ShouldInt=X< B ,SlotCustom ,long>; //B::Mytype =int , result:int
using ShouldLong=X< C ,SlotCustom ,long>;//C::Mytype not exist, result:long
std::cout<< std::is_same_v<ShouldInt, int> <<std::cout; //should true
std::cout<< std::is_same_v<ShouldLong, long> <<std::cout; //should true
}
My objective is to create a library typedef X< Param1 ,SlotCustom ,DefaultType> that means as the following pseudo code:-
if ( SlotCustom<Param1> has meaning) return "SlotCustom<Param1>" ;
else return "DefaultType"; //i.e. by default
How to do it?
Here is a similar question.
The main difference is that X<T> there can be only a bool, and many things are hardcoded.
I am new to template specialization. The solution might be obvious, but I can't find it.
If I understand your question correctly, then your approach can be made to work, for example
template <template <class> class Slot, class DefaultType>
struct GetType
{
template <typename T>
static Slot<T>&& deduce(T&&);
static DefaultType&& deduce(...);
template <typename T>
using type = std::remove_reference_t<decltype(deduce(std::declval<T>()))>;
};
template <class T, template <class> class Slot, class DefaultType>
using X = typename GetType<Slot, DefaultType>::template type<T>;
live demo here
The problem with your initial attempt was that the call to your check function in the expression for decltype() needed some argument for overload resolution to take place so that the SFINAE magic can happen. My example above relies on std::declval to introduce a dummy argument of the necessary type. Also, note that my helper functions use references rather than passing the types by value directly. This is so that it also works with types that are not copyable. Note that there will be problems if Slot<T> or the DefaultType are reference types themselves. One would have to, e.g., introduce additional wrapper types to deal with that…
Alternatively, you could use partial class template specialization to pick the correct type, for example:
template <class T, template <class> class Slot, class DefaultType, typename = void>
struct GetType
{
using type = DefaultType;
};
template <class T, template <class> class Slot, class DefaultType>
struct GetType<T, Slot, DefaultType, std::void_t<Slot<T>>>
{
using type = Slot<T>;
};
template <class T, template <class> class Slot, class DefaultType>
using X = typename GetType<T, Slot, DefaultType>::type;
live demo here
The trick here lies in the use of the last template parameter with default argument void. Due to the way the matching of partial class template specializations works (see, e.g., this answer), the specialization will only be picked if Slot<T> is a valid type. Note that above solution requires C++17. If you have to stay within C++14 (which you probably don't, given that your own example relies on C++17), you can, e.g., provide your own implementation of void_t (as explained here):
template <typename... T> struct make_void { using type = void; };
template <typename... T> using void_t = typename make_void<T...>::type;
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.