C++ template parameter pack expansion of alias - c++

So, my motivation here is to determine whether the same named type declaration within several classes are the same type. In this example, I'm looking to see that all of Foo, Bar, and Baz have an internal type Q.
#include <type_traits>
template <typename N,typename ...Ns>
using equal_type_t = typename std::enable_if_t<(std::is_same_v<N, Ns> && ...), N>;
template <typename N>
using ExtractQ_t = typename N::Q;
template <typename ...Ns>
using EqualQ_t = equal_type_t<ExtractQ_t<Ns>...>;
int main()
{
struct Qness{};
struct Foo{using Q = Qness;};
struct Bar{using Q = Qness;};
struct Baz{using Q = Qness;};
using F = EqualQ_t<Foo,Bar,Baz>;
static_assert(std::is_same_v<F,Qness>);
return 0;
}
Tested in clang9 (praise be to godbolt).
The error reported is:
#1 with x86-64 clang 9.0.0
<source>:10:31: error: pack expansion used as argument for non-pack parameter of alias template
using EqualQ_t = equal_type_t<ExtractQ_t<Ns>...>;
I could probably solve this by way of doing some template recursion, but I'm trying to learn to use parameter pack expansion wherever possible.
Is this possible? Is this not an allowed context? If I separate out a few individual N types, it works fine:
template <typename N1,typename N2, typename N3, typename ...Ns>
using EqualQ_t = equal_type_t<ExtractQ_t<N1>,ExtractQ_t<N2>,ExtractQ_t<N3>>;
I have to be having a pre-coffee brain-fog and can't see where I might be hosing the syntax.
Is there an expansion variant of this that will work?

The error diagnostic tries to say that the first parameter of equal_type_t cannot be a pack, yet you are expanding a pack into it. Thus, the simple fix is to do the same thing you did earlier:
template <typename N, typename ...Ns>
using EqualQ_t = equal_type_t<ExtractQ_t<N>, ExtractQ_t<Ns>...>;
https://godbolt.org/z/j6_HGU
The unpacking into a non-pack + pack would require template argument deduction, but that doesn't happen for alias templates, see cppreference. You would need a struct template specialization (or template function call) to get deduction.
Using SFINAE seems a little weird in this case though. If the condition is not fulfilled, you get some compiler gibberish about SFINAE thrown in your face. There are other ways to cause a hard error during compilation.
I would say the following is the idiomatic way to write the same code, which gives you a good error when there is a problem and would (not exactly coincidentally) avoid your original problem:
template <typename ...Ns>
struct equal_type;
template <typename N,typename ...Ns>
struct equal_type<N, Ns...>
{
static_assert((std::is_same_v<N, Ns> && ...), "These types must be the same!");
using type = N;
};
template <typename ...Ns>
using equal_type_t = typename equal_type<Ns...>::type;
template <typename N>
using ExtractQ_t = typename N::Q;
template <typename ...Ns>
using EqualQ_t = equal_type_t<ExtractQ_t<Ns>...>;
https://godbolt.org/z/u52mUE
For completeness, the pre-C++17 way (before fold expressions existed) does indeed use recursion:
template <typename N1, typename N2, typename ...Ns>
struct equal_type
{
static_assert(std::is_same_v<N1, N2>, "These types must be the same!");
using type = typename equal_type<N1, Ns...>::type;
};
template <typename N1, typename N2>
struct equal_type<N1, N2>
{
static_assert(std::is_same_v<N1, N2>, "These types must be the same!");
using type = N1;
};
https://godbolt.org/z/NKmMZD

Figured this was best suited to a self-answer, but this is mostly directed at Max, who asked a return question, and this response 'is too long to fit in the margins'.
If I'd never tried parameter packs, I probably wouldn't have gotten to this variation on template recursion to solve my problem, but it's probably where I might have gone to if I hadn't been educated on the real issue.
#include <type_traits>
template <typename N,typename ...Ns>
using equal_type_t = typename std::enable_if_t<(std::is_same_v<N, Ns> && ...), N>;
template <typename N>
using ExtractQ_t = typename N::Q;
template <typename N,typename ...Ns>
class EqualQ
{
public:
using type = equal_type_t<ExtractQ_t<N>,typename EqualQ<Ns...>::type>;
};
template <typename N>
class EqualQ<N>
{
public:
using type = ExtractQ_t<N>;
};
template <typename ...Ns>
using EqualQ_t = typename EqualQ<Ns...>::type;
int main()
{
struct Qness{};
struct Foo{using Q = Qness;};
struct Bar{using Q = Qness;};
struct Baz{using Q = Qness;};
using F = EqualQ_t<Foo,Bar,Baz>;
static_assert(std::is_same_v<F,Qness>);
return 0;
}
Yes, I realize it's not idiomatic, definitely less clean than either of Max's solutions, and doesn't answer my original question, but explores what I was trying to avoid.
One of the things I did discover doing it in this manner though was that aliases can't be specialized like class templates can. So point of education there too. I had to turn EqualQ into a templated struct.
The thing is, doing it this way wouldn't have educated me on why I couldn't unpack my parameter packs the way I originally wanted to, and certainly not to Max's idiomatic pattern, which I shall now be adopting. :)

Related

Does there exist a type level left-fold metafunction in the standard library?

If a,b,c,.. denote types then let (a,b) be the type std::pair<a,b> I am looking for the map, F, such that
F : ((((a,b),c),d),...))) -> std::tuple<a,b,c,d...>
Does this exist under some existing name in the standard library? If not, is there another library
in which it does or is this easily implemented and I'm just too dumb to know how to do it?
I think this one basically has to be recursive. There is no such thing in the standard library, and for once I can't come up with a Boost.Mp11 one-liner.
namespace impl {
template <typename T>
struct unfold_t {
using type = std::tuple<T>;
};
template <typename A, typename B>
struct unfold_t<std::pair<A, B>> {
using type = mp_push_back<typename unfold_t<A>::type, B>;
};
}
template <typename T>
using unfold = typename impl::unfold_t<T>::type;
With an assist from T.C., the newest edition of Mp11 (1.73) has an algorithm named mp_iterate which we can use thusly.
Given std::pair<std::pair<X, Y>, Z>, if we apply mp_first repeatedly (as R) we get the sequence:
std::pair<std::pair<X, Y>, Z>
std::pair<X, Y>
X
Then, if we apply mp_second to that sequence, we get:
Z
Y
ill-formed
That's pretty close. We do need that X. So we need a more complex metafunction for F: we need to get the second value if possible. There's a metafunction for that:
template <typename L>
using second_or_self = mp_eval_or<L, mp_second, L>;
And now mp_iterate<T, second_or_self, mp_first> gives us mp_list<Z, Y, X>. All we need to do at that point is reverse it and turn it into a std::tuple:
template <typename L>
using unfold = mp_rename<mp_reverse<
mp_iterate<L, second_or_self, mp_first>>,
std::tuple>;
Now that Boost.173 is on compiler-explorer, demo.
Or, I suppose, if you really hate your readers, you can make this a single alias:
template <typename L>
using unfold = mp_rename<mp_reverse<
mp_iterate_q<L,
mp_bind<mp_eval_or_q, _1, mp_quote<mp_second>, _1>,
mp_quote<mp_first>>>,
std::tuple>;
I like the solution by #Barry, and in particular the name unfold for this meta-function. However, I think you can implement this quite easily using partial specializations, without using boost.Mp11, and you might find this easier to understand.
First, provide the base case for a type that's already completely unfolded into a tuple:
template <typename ...Ts>
struct unfold_impl { using type = std::tuple<Ts...>; };
and then provide a specialization if there's still a pair left to unfold:
template <typename A, typename B, typename ...Ts>
struct unfold_impl<std::pair<A,B>, Ts...> : unfold_impl<A, B, Ts...> {};
and finally provide a convenience alias template:
template <typename ...Ts>
using unfold = typename unfold_impl<Ts...>::type;
and that's it.
You can check that it works, like this:
static_assert(
std::is_same_v<
unfold<std::pair<std::pair<std::pair<int, long>, char>, double>>,
std::tuple<int, long, char, double>
>);
Here's a demo

How do I "expand" a compile-time std::array into a parameter pack?

I'd like to use partial template specialization in order to 'break down' an array (which is created at compile time) into a parameter pack composed of its values (to interface with other structures I define in my code). The following (my first attempt) is not compiling
#include <array>
template <typename T, auto k> struct K;
template <typename T, std::size_t... A> struct K<T, std::array<std::size_t, sizeof...(A)>{A...}> {};
because the template argument std::array<long unsigned int, sizeof... (A)>{A ...} must not involve template parameters. As I understood it, it is not possible to provide non type parameters in a partial template specialization if they non-trivially depend on template parameters. Hence I attempted to work around this issue by containing the value in a type:
#include <array>
template <auto f> struct any_type;
template <typename T, typename array_wrapper> struct FromArr;
template <typename T, std::size_t... A>
struct FromArr<T, any_type<std::array<std::size_t, sizeof...(A)>{A...}>> {};
int main() {
FromArr<int, any_type<std::array<std::size_t, 2>{1, 2}>> d;
(void) d;
}
However, here, the partial template specialization fails when I'm trying to use it; the definition above does not match the way I use it, and I am unsure why. It fails with the following error:
file.cc: In function ‘int main()’:
file.cc:10:55: error: aggregate ‘FromArr<int, Any<std::array<long unsigned int, 2>{std::__array_traits<long unsigned int, 2>::_Type{1, 2}}> > d’ has incomplete type and cannot be defined
10 | FromArr<int, Any<std::array<std::size_t, 2>{1, 2}>> d;
Is it possible to work around this / use a different approach in order to interface the array as parameter pack?
Used Compiler
I use g++-10.0 (GCC) 10.0.1 20200124 (experimental) and compile via g++ -std=c++2a file.cc, c++2a is required because I use non-type template parameters.
Edit:
Description how the array is ment to be processed
In my real code I have got a structure which depends on -- among others -- a parameter pack (1). It would be nice if I were able to use an array (2) (which I have got in another piece of my code as a non-type template argument) to interface with that structure, as sketched in the code below.
template <int... s> struct toBeUsed; // (1)
template <std::size_t s, std::array<int, s> arr> struct Consumer { // (2)
toBeUsed<arr> instance; // This is what I would like to do
}
My Attempt is to write a helper struct as above FromStruct, which I can instantiate with an array in which I have a typedef FromStruct::type that provides toBeUsed with the correct arguments, similar to this example, which does what I want to do here with the types a std::tuple is composed of.
Link to the example
here I link the simplified usage example (2nd code block).
Inspired by #dfri 's answer, I transformed her / his solution to a version which can omit functions, but instead uses only one struct using partial template specialization for the std::integer_sequence which might also be interesting to others:
template <auto arr, template <typename X, X...> typename Consumer,
typename IS = decltype(std::make_index_sequence<arr.size()>())> struct Generator;
template <auto arr, template <typename X, X...> typename Consumer, std::size_t... I>
struct Generator<arr, Consumer, std::index_sequence<I...>> {
using type = Consumer<typename decltype(arr)::value_type, arr[I]...>;
};
Full example with usage:
#include <array>
/// Structure which wants to consume the array via a parameter pack.
template <typename StructuralType, StructuralType... s> struct ConsumerStruct {
constexpr auto operator()() const { return std::array{s...}; }
};
/// Solution
template <auto arr, template <typename X, X...> typename Consumer,
typename IS = decltype(std::make_index_sequence<arr.size()>())> struct Generator;
template <auto arr, template <typename X, X...> typename Consumer, std::size_t... I>
struct Generator<arr, Consumer, std::index_sequence<I...>> {
using type = Consumer<typename decltype(arr)::value_type, arr[I]...>;
};
/// Helper typename
template <auto arr, template <typename T, T...> typename Consumer>
using Generator_t = typename Generator<arr, Consumer>::type;
// Usage
int main() {
constexpr auto tup = std::array<int, 3>{{1, 5, 42}};
constexpr Generator_t<tup, ConsumerStruct> tt;
static_assert(tt() == tup);
return 0;
}
A C++20 approach
See OP's own answer or, for possibly instructive but more verbose (and less useful) approach, revision 2 of this answer.
A C++17 approach
(This answer originally contained an approach using a minor C++20 feature (that a lambda without any captures may be default constructed), but inspired by the original answer the OP provided a much neater C++20 approach making use of the fact that a constexpr std::array falls under the kind of literal class that may be passed as a non-type template parameter in C++20 (given restraints on its ::value_type), combined with using partial specialization over the index sequence used to unpack the array into a parameter pack. This original answer, however, made use of a technique of wrapping std::array into a constexpr lambda (>=C++17) which acted as a constexpr (specific) std::array creator instead of an actual constexpr std::array. For details regarding this approach, see revision 2 of this answer)
Following OP's neat approach, below follows an adaption of it for C++17, using a non-type lvalue reference template parameter to provide, at compile time, a reference to the array to the array to struct target.
#include <array>
#include <cstdlib>
#include <tuple>
#include <type_traits>
#include <utility>
// Parameter pack structure (concrete target for generator below).
template <typename StructuralType, StructuralType... s>
struct ConsumerStruct
{
// Use tuple equality testing for testing correctness.
constexpr auto operator()() const { return std::tuple{s...}; }
};
// Generator: FROM std::array TO Consumer.
template <const auto& arr,
template <typename T, T...> typename Consumer,
typename Indices = std::make_index_sequence<arr.size()> >
struct Generator;
template <const auto& arr,
template <typename T, T...> typename Consumer,
std::size_t... I>
struct Generator<arr, Consumer, std::index_sequence<I...> >
{
using type =
Consumer<typename std::remove_cv<typename std::remove_reference<
decltype(arr)>::type>::type::value_type,
arr[I]...>;
};
// Helper.
template <const auto& arr, template <typename T, T...> typename Consumer>
using Generator_t = typename Generator<arr, Consumer>::type;
// Example usage.
int main()
{
// As we want to use the address of the constexpr std::array at compile
// time, it needs to have static storage duration.
static constexpr std::array<int, 3> arr{{1, 5, 42}};
constexpr Generator_t<arr, ConsumerStruct> cs;
static_assert(cs() == std::tuple{1, 5, 42});
return 0;
}
Note that this approach places a restriction on the std::array instance in that it needs to have static storage duration. If one wants to avoid this, using a constexpr lambda which generates the array may be used as an alternative.

Is it possible to have a "generic" template parameter in C++, that can be either a non-type template parameter or a type?

In C++, it's possible to use a type as a template parameter, e.g:
template <typename T>
void MyFn();
It's also possible to use a non-type as a template parameter in some cases, e.g.:
template <int64_t T>
void MyFn2();
My question is whether it's possible to have a "generic" template parameter that can be both? Like:
template <TypenameOrint64_t T>
void MyFn3();
such that both MyFn3<42> and MyFn3<double> would be acceptable.
An example of how I might use this:
template <typename ValType, ValType Head, ValType ...Tail>
struct ListS{
template <typename OutType, template <ValType ArgType> class Fn>
using MapHead = ListS<OutType, Fn<Head>::val, Tail...>;
};
template<int64_t N>
struct SquareS{
static constexpr const int64_t val = N * N;
};
using Sqrd = ListS<int64_t, 3, 4>::MapHead<int64_t, SquareS>;
static_assert(std::is_same<Sqrd, ListS<int64_t, 9, 4>>::value, "Values don't match");
The above is a very rough sketch of a compile-time list of values along with a single compile-time "function" on it. Would it be possible to make something like that also support lists of types, not just lists of non-type template param compatible values, without just duplicating all the code?
Is it possible to have a “generic” template parameter in C++, that can be either a non-type template parameter or a type?
Short answer: no.
Long answer.
No. The best I can imagine to mix types and values is wrap values in types, using std::integral_constant, by example.
So, your desired code, could be written (C++17) almost as follows
#include <utility>
template <typename ...>
struct ListS;
template <typename ValType, ValType Head, ValType ...Tail>
struct ListS<std::integral_constant<ValType, Head>,
std::integral_constant<ValType, Tail>...>
{
template <template <auto> class Fn, typename OutType = ValType>
using MapHead = ListS<std::integral_constant<OutType, Fn<Head>::value>,
std::integral_constant<OutType, Tail>...>;
};
template <auto N>
struct SquareS : public std::integral_constant<decltype(N), N*N>
{ };
int main ()
{
using T1 = ListS<std::integral_constant<long long, 3ll>,
std::integral_constant<long long, 4ll>>;
using T2 = T1::MapHead<SquareS>;
using T3 = ListS<std::integral_constant<long long, 9ll>,
std::integral_constant<long long, 4ll>>;
static_assert( std::is_same_v<T2, T3> );
}
Pre C++17 you can't use auto for the type of the template values so you should make some simple corrections.
You could use function overloading and the auto type deduction that came with C++17
to accomplish something similar.
template<typename myType>
auto myFn3(myType value){
return value;
}
template<auto value> //takes any non-type parameter
auto myFn3(){
return value;
}
int main(){
auto test1_normal = myFn3(3);
auto test1_cast = myFn3<double>(3); //able to perform a cast
auto test1_auto = myFn3<3>();
return 0;
}

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.

Using non-type template argument during partial specialization

Consider the following structs:
//Implementations provided elsewhere
struct A { A(int i, double d, std::string s); /* ... */ };
struct B { B(double d1, double d2); /* ... */ };
I have two conversion classes whose template signatures look like:
TupleAs< A, int, double, std::string > via1 { ... };
ArrayAs< B, double, 2 > via2 { ... };
Predictably, TupleAs converts a triplet of int,double, and std::string values into an object of type A. Similarly, ArrayAs converts a pair of two double values into an object of type B. (And yes, there are reasons why I cannot call the A and B constructors directly.)
Improving the syntax
I would like to change the syntax so I can do the following:
TupleAs< A(int,double,std::string) > via1 { ... };
ArrayAs< B(double,2) > via2 { ... };
which, I think, is more descriptive of a conversion process. The TupleAs template declaration and corresponding partial specialization would look like this:
template <typename T> struct TupleAs;
template <typename T, typename ... Args>
struct TupleAs<T(Args...)> { ... };
Compiler errors
However, if I try to do something similar with the ArrayAs version:
template <typename T> struct ArrayAs;
template <typename T, typename U, unsigned N>
struct ArrayAs<T(U,N)> { ... };
I get the following errors in clang (3.6) when trying to instantiate it (ArrayAs< B(double,2)> test;):
typeAs.cpp:14:22: error: unknown type name 'N'
struct ArrayAs<T(U,N)>{
^
typeAs.cpp:14:10: warning: class template partial specialization contains a template parameter that cannot be deduced; this partial specialization will never be used
struct ArrayAs<T(U,N)>{
^~~~~~~~~~~~~~~
typeAs.cpp:13:45: note: non-deducible template parameter 'N'
template<typename T, typename U, unsigned N>
^
The gcc error diagnostic is a little different, but I won't post it here.
I admit that my templating skills should be better than they are, and I also concede that an analogous std::function<B(double,2)> declaration clearly is nonsense. But can someone tell me why the particular syntax I'm trying to achieve is not allowed? I looked through the C++14 standard and had trouble finding the relevant portion, and I'm having trouble interpreting the clang diagnostic message.
When you specialize TupleAs:
template <typename T, typename ... Args>
struct TupleAs<T(Args...)>
You are basically overloading the notation for a function. You are specializing on a function that takes Args... and returns a T. That is a type. You may not be using that function as a function, or really ever think about it as being a type, but that is what it is.
On the other hand, here:
template <typename T, typename U, unsigned N>
struct ArrayAs<T(U,N)> { ... };
There is no such thing as a function that takes N. It could take unsigned, but it can't take a value. There is just no such reasonable thing. From your example, B(double, 2) simply does not make sense. At best, you could write something that would allow:
template <unsigned N> using size_ = std::integral_constant<size_t, N>;
ArrayAs< B(double,size_<2>) >
Or even:
ArrayAs< B(std::array<double, 2>) >
since now we're back to using types everywhere. Whether you prefer that or not is personal preference.
The key here is that types are first-class citizens when it comes to all things template metaprogramming, and values should be avoided where possible.
template <typename T> struct ArrayAs;
template <typename T, typename U, std::size_t N>
struct ArrayAs<T(std::array<U,N>)> { ... };
works, as would:
template<class T>
struct to_array;
template<class T, size_t N>
struct to_array< T[N] > { using type = std::array<T, N>; };
template<class T>
using arr = typename to_array<T>::type;
then:
ArrayAs< Bob( arr<int[3]> ) > some_var;
live example.
Sadly, directly using ArrayAs< Bob( int[3] ) > doesn't work due to how arrays in function types decay to pointers.