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.
Related
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.
I have a class that takes a size type as a parameter in case one doesn't want to use size_t or another hard coded type which would require casting and checking for overflows.
This class also has some methods taking a second template type :
template< typename SizeType = uint32_t >
class BufferReader
{
SizeType m_nPosition;
template< typename T >
T Read();
}
My problem is, what is the syntax to implement this ?
template< typename SizeType, typename T >
T BufferReader< SizeType >::Read()
{
// ...
}
Which gives me an error :
invalid use of incomplete type 'class core::BufferReader<SizeType>'
Or :
template< typename T >
template< typename SizeType >
T BufferReader< SizeType >::Read()
{
// ...
}
Which gives the same error.
Or anything else ?
Thank you !
Your second example is nearly right, you just need to have the template parameters of the class first and those of the function second:
template< typename SizeType >
template< typename T >
T BufferReader< SizeType >::Read()
{
// ...
}
You almost got it. You have to stack the templates as in the declaration: first the class and then the member.
template < typename SizeT >
template < typename T >
T
BufferReader<SizeT>::Read()
{
return T {};
}
Note that the code can be simplified by defining the function right in the class body but I understand that some people will prefer to separate declaration from definition for aesthetic reasons.
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;
I have rather simple, I imagine, question about CRTP, but I cannot seem to find an answer to it. Probably, because it is so simple, no one thought of asking it. I am new to the concept, so, please don't laugh too hard ;).
Here is the code (it's sort of an attempt to do something similar to STL containers):
template< typename tT >
struct TBase
{
typedef tT T;
};
template< typename tTBase >
struct TTraitsBase
{
typedef typename tTBase::T T;
};
template< typename tTHelpee, typename tTTraits >
struct THelper
{
typedef typename tTTraits::T T;
typedef typename tTHelpee::T Th; /* This generates a compiler error:
'T' not being a member of TDerived<tT> */
T Foo( void )
{
return static_cast< tTHelpee* > ( this )->mVal;
}
};
template< typename tT >
struct TDerived : TBase< tT > , THelper< TDerived< tT > , TTraitsBase< TBase< tT > > >
{
using TBase< tT >::T;
T mVal;
};
int main()
{
TDerived< int >::T lTmp = -1;
TDerived< int > lObj;
lObj.mVal = -1;
std::cout << lObj.Foo() << std::endl;
return 0;
}
Everything compiles if I comment the offending typedef typename _THelpee::T Th;. And that what confuses me: if compiler does not like typedef typename _THelpee::T Th;, why does it let through static_cast< _THelpee* > ( this )->mVal? I assume, it has something to do with not being able to instantiate THelper when instantiating TDerived, but no clear understanding. Can someone, please, give a brief explanation and/or some references on what is happening here? Thank you.
EDIT: removed '_T' prefix.
Implicitly instantiating a class does not instantiate its member function definitions. Each member of a class template is instantiated only when used (unless you use explicit instantiation).
The first thing you implicitly instantiate is TDerived<int>, on the first line of main. To instantiate that, the compiler looks up the TDerived template, sees that there are a couple of dependent base classes, so tries to instantiate those. TBase<int> instantiates with no problem. But on trying to instantiate THelper< TDerived<int>, TTraitsBase< TBase<int> > >, there's an attempt to get a member TDerived<int>::T, but TDerived<int> is still an incomplete type.
Instantiating THelper< ... > also notes that there is a member function int Foo(), but does not evaluate the semantics of its definition.
By the time you call lObj.Foo() from main, which instantiates that int THelper< ... >::Foo(), TDerived<int> is a complete type, so there's no problem there.
There are couple of problem with the code posted. Since, I couldn't make out the intention, I will list what I found erroneous.
(1) Using an incomplete type:
template< typename _T >
struct TDerived : TBase< _T > , THelper< TDerived< _T > , TTraitsBase< TBase< _T > > >
^^^^^^^^^^^^^^ it's incomplete
IMO when you are defining the body of TDerived<_T>, you should not use the same as a parameter to anything.
(2) Improper type definition.
using TBase< _T >::T;
should be,
typedef typename TBase< _T >::T T;
I'd say you have a circular dependency in your CRTP (as one would have, by its very nature), but that means that the base template class isn't allowed to use any of its parameter class, because that very parameter class isn't defined yet - it's still an incomplete type at the time where Base<T> is instantiated.
So, this is OK:
template <typename T> struct Base
{
T * impl; // incomplete type is OK
};
class Foo : public Base<Foo> { /* ... */ };
But this isn't:
template <typename T> struct Base
{
T x; // need to know T, but T depends on Base<T>!
};
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 > {};