is it possible to write a generalized rebind template? - c++

without specializing for each class template/class, is it possible to write a generalized 'rebind' meta function, so that
given
template<class > struct foo;
struct bar;
the following
is_same<rebind<foo<int>,float>,foo<float>>
is_same<rebind<bar>,bar>
and maybe
is_same< rebind<std::vector<int>,float>,std::vector<float>>
returns a type equivalent of true?

Sure.
But be aware that any template template parameter taking a variadic template parameter list is restricted to accepting templates with only type parameters, not non-type parameters. In other words, the general case below won't work for std::array because its second argument is an integer. You'd have to add a special case.
The primary template is already a special case, since it handles classes that aren't a specialization of a template.
http://liveworkspace.org/code/5b6f0cb3aec1eb74701e73d8d21aebab
template< typename bound, typename ... new_args >
struct rebind_class {
static_assert( sizeof ...( new_args ) == 0,
"can't rebind arguments to non-specialization" );
typedef bound type;
};
template< template< typename ... > class template_, typename ... new_args,
typename ... old_args >
struct rebind_class< template_< old_args ... >, new_args ... > {
typedef template_< new_args ... > type;
};
template< typename ... args >
using rebind = typename rebind_class< args ... >::type;

Related

concept in header doesn't constrain a parameter pack in C++20?

Context: I am rewriting a library that worked with the GCC -fconcepts to C++20. Clang 10 and GCC 10 give me the same unexpected problem, so it's probably my fault.
I have a class template that supports two cases. It can either be created from a list of pin_out's, or from a list of port_out's.
template< typename T > concept pin_out = T::is_pin_out;
template< typename... Ts > concept pin_out_list = ( pin_out< Ts > && ... );
template< typename T > concept port_out = T::is_port_out;
template< typename... Ts >
requires pin_out_list< Ts...> || ( port_out< Ts > && ... )
struct port;
When I write the specialization for a list of pins_out's, with the concepts TS I could write
template< pin_out_list... Ts >
struct port< Ts... > {};
but now with C++20 the compilers complain that the specialization is not more constrained than the base. When I add a requires clause it does compile.
template< pin_out_list... Ts >
requires pin_out_list< Ts... >
struct port< Ts... > {};
And I can remove the pin_out_list... from the template header.
template< typename... Ts >
requires pin_out_list< Ts... >
struct port< Ts... > {};
Is the pin_out_list... in the specialization now silently ignored?
test it on compiler explorer
One of the many things that P1141 changed was what a variadic constraint actually means:
In [temp.param]/11 we have:
template <C2... T> struct s3; // associates C2<T...>
This seems to be doing an unexpected thing, which is having the constraint apply to more than one type in a pack at a time.
And as a result of that paper, a variadic constraint like that now applies to every type in the back. That is, we now have (this is in [temp.param]/5 now):
template <C2... T> struct s3; // associates (C2<T> && ... )
As a result, this specialization:
template< pin_out_list... Ts >
struct port< Ts... > {};
means:
template <typename... Ts> requires (pin_out_list<Ts> && ...)
struct port<Ts...>;
and not:
template <typename... Ts> requires pin_out_list<Ts...>
struct port<Ts...>;
You need the latter meaning (this is the constraint in the primary expression) so you need to write the latter syntax. The compiler wasn't silently ignoring your specialization.

How do you typedef a function pointer type with parameter pack arguments

What's the syntax to typdef a parameter pack into a function pointer?
I want to be able to typedef a function pointer, but the compiler complains when I do something like this
template< class ...Args >
struct method { typedef typename void(*type)(void*, Args...); };
with a message along the lines of error: expected nested-name-specifier before 'void'
It works fine without typename. http://coliru.stacked-crooked.com/a/64b3fbec9276dd70
You shouldn't use typename here because there is no nested-name-specifier.
I suppose that you should remove typename from the typedef line
template <typename ... Args>
struct method
{ typedef void(*type)(void*, Args...); };
Another solution could be using using instead of typedef (IMHO is a little clearer)
template <typename ... Args>
struct method
{ using type = void(*)(void*, Args...); };

variadic template pack within decltype

I may write as
template< class T0> struct Last0
{
using type = decltype(T0{}); // OK compiles. `type = T0`
};
template< class T0, class T1> struct Last1
{
using type = decltype(T0{}, T1{}); // OK, compiles. `type = T1`
};
template< class T0, class T1, class T2> struct Last3{
using type = decltype(T0{}, T1{}, T2{}); // Ok, compiles. `type = T2`
};
But, when I use variadic templates, it's not compiled:
template< class ... T> struct Last{
using type = decltype(T{} ... ); //<--- Error !!!
};
What's problem?
There is a taxative list of language constructs where pack expansion can happen (C++11, 14.5.3ยง4). With the exception of sizeof..., it's always in constructs where the comma , is a grammatical separator of a list, and not an operator. An expression cannot be a pack expansion.
To get the last type in a pack, you can do this:
template <class Head, class... Tail>
struct Last {
typedef typename Last<Tail...>::Type Type;
};
template <class Head>
struct Last<Head> {
typedef Head Type;
};
You can only apply decltype to an expression, not to a pack. Packs are very special and basically always need to be expanded. You essentially have the same problem as not being able to store packs directly: using type = T... isn't allowed, either.
The standard solution is to store packs inside some "container template", typically tuple:
using T = std::tuple<decltype(T{})...>;

Partial template specialization with multiple template parameter packs

Continuing my journey into the world of variadic templates, I encountered another problem.
Assuming the following template class:
template < typename T >
struct foo
{
//default implementation
};
it is possible to partially specialize it for variadic template instantiations like this:
template < template < typename ... > class T, typename ...Args >
struct foo< T< Args... > >
{
//specialized implementation
};
With this, foo< int > will correspond to the default implementation and foo< std::tuple< int, char > > to the specialized implementation.
However, things become more complicated when using several template parameters. For example, if we have the following template class
template < typename T, typename U >
struct bar {};
and we want to partially specialize it as we did for foo, we cannot do
template < template < typename ... > class T, typename ...TArgs,
template < typename ... > class U, typename ...UArgs >
struct bar< T< TArgs... >, U< UArgs... > > {};
//This would correspond to the specialized version with
//T=std::tuple,
//TArgs=int,char
//U=std::tuple,
//UArgs=float
bar< std::tuple< int, char >, std::tuple< float > > b;
Indeed, if I am correct, we can only have one template parameter pack and it must be positioned at the end of the parameter list. I understand why this is mandatory in template declarations, but for certain partial template specialization (like the example above), this should not be an issue.
Is it possible to achieve partial template specialization with multiple template parameter packs?
Edit: Now I feel silly...the code I gave above compiles perfectly (at least with gcc 4.5). The compile error I had was not because of multiple parameter packs, but because of their use as member functions parameters. In the partial specialization of bar, I tried to define a member function that takes both TArgs and UArgs parameters:
template < template < typename ... > class T, typename ...TArgs,
template < typename ... > class U, typename ...UArgs >
struct bar< T< TArgs... >, U< UArgs... > >
{
void method( TArgs... targs, UArgs... uargs ) //compile error here
{
}
};
On the member function declaration, gcc gives me the error
parameters packs must be at the end of the parameter list.
As far as I can tell, the compiler should be able to define the correct member function for a given template instantiation, e.g. bar< std::tuple< int, char >, std::tuple< float > > should contain a member function void method( int, char, float ). Am I doing something wrong? Or am I trying to do something that is not possible? If so, is there a good reason why this is not possible?
Probably this answer won't clear your question directly,
but the following code compiled on ideone(gcc-4.5.1) when I tested.
#include <cstdio>
#include <tuple>
template< class, class > struct S {
S() { puts("primary"); }
};
template<
template< class... > class T, class...TArgs
, template< class... > class U, class...UArgs
>
struct S< T< TArgs... >, U< UArgs... > > {
S() { puts("specialized"); }
};
int main()
{
S< int, int > p; // "primary"
S< std::tuple< int, char >, std::tuple< float > > s; // "specialised"
}
I'm not sure this code is strictly conformant, but
as far as I read N3225 14.5.3, I couldn't find the statement which mentions
that template parameter pack has to be the last template parameter.
Edit:
I reread N3225 and found the following statements:
8.3.5/4 If the parameter-declaration-clause
terminates with an ellipsis or a
function parameter pack (14.5.3), the
number of arguments shall be equal to
or greater than the number of
parameters that do not have a default
argument and are not function
parameter packs.
14.8.2.5/10 [Note: A function parameter pack can only occur at the
end of a
parameter-declarationlist(8.3.5). -end
note]
So, as you mentioned, function parameter pack has to be the last parameter
unfortunately.
A non-template member function of a class template is an ordinary function
for that class when it is instantiated(fully specialized).
So I wish that the code in this question can be compiled logically, as a
special case.

Check if parameter pack contains a type

I was wondering if C++0x provides any built-in capabilities to check if a parameter pack of a variadic template contains a specific type. Today, boost:::mpl::contains can be used to accomplish this if you are using boost::mpl::vector as a substitute for variadic templates proper. However, it has serious compilation-time overhead. I suppose, C++0x has compiler-level support for std::is_same. So I was thinking if a generalization like below is also supported in the compiler.
template <typename... Args, typename What>
struct is_present
{
enum { value = (What in Args...)? 1 : 0 };
};
Fortunately, the C++ standard has evolved. With C++1z aka C++17, you can finally iterate easily over parameter packs. So the code for the answer is (almost) as simple, as suggested in the question:
template<typename What, typename ... Args>
struct is_present {
static constexpr bool value {(std::is_same_v<What, Args> || ...)};
};
The weird-looking (std::is_same_v<What, Args> || ...) is expanded by the compiler internally to (std::is_same_v<What, Args[0]> || std::is_same_v<What, Args[1]> || ...), which is exactly, what you want. It even correctly yields false with an empty Args parameter pack.
It is even possible to do the whole check inline in a function or method - no helper structs are required anymore:
template<typename T, typename ... List>
void foo(T t, List ... lst)
{
if constexpr((std::is_same_v<T, List> || ...)) {
std::cout << "T is in List" << std::endl;
} else {
std::cout << "T is not in List" << std::endl;
}
}
Note: This has been taken from another question, that was marked as a duplicate of this question. As this is the "canonical" question for this topic, I added that important information here.
No, you have to use (partial) specialization with variadic templates to do compile-time computations like this:
#include <type_traits>
template < typename Tp, typename... List >
struct contains : std::true_type {};
template < typename Tp, typename Head, typename... Rest >
struct contains<Tp, Head, Rest...>
: std::conditional< std::is_same<Tp, Head>::value,
std::true_type,
contains<Tp, Rest...>
>::type {};
template < typename Tp >
struct contains<Tp> : std::false_type {};
There is only one other intrinsic operation for variadic templates and that is the special form of the sizeof operator which computes the length of the parameter list e.g.:
template < typename... Types >
struct typelist_len
{
const static size_t value = sizeof...(Types);
};
Where are you getting "it has serious compilation-time overhead" with boost mpl from? I hope you are not just making assumptions here. Boost mpl uses techniques such as lazy template instantiation to try and reduce compile-times instead of exploding like naive template meta-programming does.
If you want to avoid manual type recursion, std::common_type appears to me to be the only utility in the STL which is a variadic template, and hence the only one which could potentially encapsulate recursion.
Solution 1
std::common_type finds the least-derived type in a set of types. If we identify numbers with types, specifically high numbers with less-derived types, it finds the greatest number in a set. Then, we have to map equality to the key type onto a level of derivation.
using namespace std;
struct base_one { enum { value = 1 }; };
struct derived_zero : base_one { enum { value = 0 }; };
template< typename A, typename B >
struct type_equal {
typedef derived_zero type;
};
template< typename A >
struct type_equal< A, A > {
typedef base_one type;
};
template< typename Key, typename ... Types >
struct pack_any {
enum { value =
common_type< typename type_equal< Key, Types >::type ... >::type::value };
};
Solution 2
We can hack common_type a little more. The standard says
A program may specialize this trait if
at least one template parameter in the
specialization is a user-defined type.
and describes exactly what is inside it: a recursive partial specialization case, a case which applies a binary operator, and a terminal case. Essentially, it's a generic fold function, and you can add whatever binary operation you please. Here I used addition because it's more informative than OR. Note that is_same returns an integral_constant.
template< typename Addend >
struct type_sum { // need to define a dummy type to turn common_type into a sum
typedef Addend type;
};
namespace std { // allowed to specialize this particular template
template< typename LHS, typename RHS >
struct common_type< type_sum< LHS >, type_sum< RHS > > {
typedef type_sum< integral_constant< int,
LHS::type::value + RHS::type::value > > type; // <= addition here
};
}
template< typename Key, typename ... Types >
struct pack_count : integral_constant< int,
common_type< type_sum< is_same< Key, Types > > ... >::type::type::value > {};