Variadic template argument within template template parameter in a partial specialization - c++

I am trying to develop a generic code that can select different containers types (std::vector, std::map, other), and perform operations over that container wrapper, but I got stuck with the following code:
enum class EContainnerType
{
EContainnerType_Normal,
EContainnerType_OR,
EContainnerType_AND
};
// Base declaration
template<EContainnerType, template<class ... > class ContainerType, class ... ArgType >
struct ConditionContainnerType
{
};
// Partial Specialization
template< template<class ... > class ContainerType, class ... ArgType >
struct ConditionContainnerType<EContainnerType::EContainnerType_OR, ContainerType<ArgType ... >, ArgType ...>
{
};
int main()
{
return 0;
}
The variadic template template argument does not compile, and I get this error:
main.cpp:33:108: error: wrong number of template arguments (2, should be 3)
struct ConditionContainnerType<EContainnerType::EContainnerType_OR,typename ContainerType<ArgType>, ArgType>
^
main.cpp:29:8: error: provided for 'template<EContainnerType <anonymous>, template<class> class ContainerType, class ArgType> struct ConditionContainnerType'
struct ConditionContainnerType
GOAL:
The main goal of this implementation is to perform a certain kind of operation of classification (OR, AND, XOR), this operation is performed on an element that is compared against the generic container.
The operation type is defined by the enum class, and a partial specialization is selected to do the action.
So, if I have a set {a,b,c,d,e} and I fill the set with a specific combination of elements say:
generic_container<Operation_type,generic_set_element> then I want the generic conditional container to perform an action selected by "Operation Type".
So if an element x is compared against the set, the generic container can perform the preselected action over the x element.

Your compiler is broken. The correct error messages look like this (g++ 4.8.2)
error: type/value mismatch at argument 2 in template parameter list for ‘template<EContainnerType <anonymous>, template<class ...> class ContainerType, class ... ArgType> struct ConditionContainnerType’
struct ConditionContainnerType<EContainnerType::EContainnerType_OR,ContainerType<ArgType ... >,ArgType ...>
^
error: expected a class template, got ‘ContainerType<ArgType ...>’
or this (clang 3.3)
error: template argument for template template parameter must be a class template or type alias template
struct ConditionContainnerType<EContainnerType::EContainnerType_OR,ContainerType<ArgType ... >,ArgType ...>
^
They are pretty self-explanatory.
So just remove the parameter.
//Partial Specialization
template< template<class ... > class ContainerType, class ... ArgType >
struct ConditionContainnerType<EContainnerType::EContainnerType_OR,
ContainerType, ArgType ...>
{
};
Now you can write
ConditionContainnerType<EContainnerType::EContainnerType_OR, std::vector, int> a;
ConditionContainnerType<EContainnerType::EContainnerType_OR, std::map, int, int> b;
without having to repeat the parameters twice.

The problem is that you cannot specialize a template-template parameter with a template-template parameter with given template arguments, like forcing:
ContainerType<Args...>
to match:
template <typename...> class BaseContainerType
because it is no longer a template template argument. Instead, a plain ContainerType name would need to be used here, without <Args...> part:
// Base declaration
template <template <typename...> class ContainerType>
struct ConditionContainnerType
{
};
// Partial specialization for two-parameter template template parameter
template <template <typename, typename> class ContainerType>
struct ConditionContainnerType<ContainerType>
{
};
You can, however, specialize the template type itself with a template-template parameter filled with arguments (even with an expanded parameter pack), which goes as follows:
// Base declaration
template <EContainnerType, typename ContainerType, typename... Args>
// ^^^^^^^^^^^^^^^^^^^^^^^ normal type here
struct ConditionContainnerType
{
};
// Partial specialization
template <template <typename...> class ContainerType, typename... Args>
// ^^^^^^^^ template here
struct ConditionContainnerType<EContainnerType::EContainnerType_OR,
ContainerType<Args...>,
// ^^^^^^^ expanded parameter pack
Args...>
{
};
or without a trailing parameter pack:
template <template <typename...> class ContainerType, typename... Args>
struct ConditionContainnerType<EContainnerType::EContainnerType_OR,
ContainerType<Args...>>
{
};
as well as specialize it for templated containers like std::vector<T, A> which take exactly two parameters: type T and allocator A:
template <template <typename, typename> class ContainerType, typename T, typename A>
struct ConditionContainnerType<EContainnerType::EContainnerType_OR,
ContainerType<T, A>,
T>
{
};
Tests:
int main()
{
// base, AND does not match OR
ConditionContainnerType<EContainnerType::EContainnerType_AND
, MyContainer<int>
, int>{};
// specialized, OR and parameter pack matches
ConditionContainnerType<EContainnerType::EContainnerType_OR
, MyContainer<int>
, int>{};
// base, OR matches, but parameters packs do not
ConditionContainnerType<EContainnerType::EContainnerType_OR
, MyContainer<float>
, int>{};
// vector, OR and parameter pack matches
ConditionContainnerType<EContainnerType::EContainnerType_OR
, std::vector<int>
, int>{};
// OR and no parameter-pack
ConditionContainnerType<EContainnerType::EContainnerType_OR
, std::vector<int>>{};
}
DEMO
If otherwise you aim to specialize your base declaration depending on concrete container type (std::vector, std::map), you can do this as follows:
// Base declaration
template <EContainnerType, template <typename...> class ContainerType>
struct ConditionContainnerType
{
};
// Partial specialization for std::vector
template <>
struct ConditionContainnerType<EContainnerType::EContainnerType_OR, std::vector>
// ^^^^^^^^^^^
{
};
// Partial specialization for std::map
template <>
struct ConditionContainnerType<EContainnerType::EContainnerType_AND, std::map>
// ^^^^^^^^
{
};
Tests:
int main()
{
// First specialization
ConditionContainnerType<EContainnerType::EContainnerType_OR, std::vector>{};
// Second specialization
ConditionContainnerType<EContainnerType::EContainnerType_AND, std::map>{};
}
DEMO 2

Related

Is it possible to create a template template parameter list?

I was wondering whether it's possible to make a list containing template template parameters, like
template <
template <typename...> class TTP0,
template <typename...> class... TTPs
>
struct TTP_List : TTP_List<TTPs...> {};
A problem I encountered is that I did not know a good way to access the elements of the list, that is, TTP0. I would like to use type aliases, typedef or using. I however know this is not possible, because the template template parameters are not types and must therefor not be treated as such.
A approach I could imagine working is making explicit structs to read the data and make it use specializations, such as:
template <template <typename...> class>
struct container_TTPs;
template <template <typename...> class TTP>
struct container_TTPs<std::vector> {};
However, this approach seems very explicit. Is there a way to accomplish this recognition without this use of template specialization?
Feel free to ask if I need to elaborate.
EDIT: For example, I want to be able to use certain expressions for every held type, such as TestClass<TTP_List<std::vector, std::list>>::value, where TestClass uses the std::vector and the std::list, without requiring a variadic template within TestClass, so that multiple TTP_Lists can be passed.
I don't understand what do you mean with "access the elements of the list".
It seems to me that you should give us an example of what do you want, concretely, do accessing elements of the list.
Anyway, as you can use using to "access" (?) typenames
template <typename T0, typename ... Ts>
struct foo
{
using type = T0;
};
you can use a template using to "access" (?) a template-template parameter as follows
template <template <typename...> class T0,
template <typename...> class ... Ts>
struct foo
{
template <typename ... As>
using templ_type = T0<As...>;
};
and you can use it in this way
// vi is a std::vector<int>
foo<std::vector, std::set, std::map>::templ_type<int> vi { 0, 1, 2 };
The problem (a problem) is that a template parameter variadic list (isn't important if of typenames, of values or of template-templates) must be in last position.
So
template <typename ... Ts, typename T0>
struct foo
{ };
is wrong, because Ts... must be in last position, and
template <typename T0, typename ... Ts>
struct foo
{ };
is correct.
With template-template parameters,
template <template <typename ...> class ... Ts,
template <typename ...> class T0>
struct foo
{ };
is wrong where
template <template <typename ...> class T0,
template <typename ...> class ... Ts>
struct foo
{ };
is correct.

Merging types from variadic template using template-template arguments

I'm trying to extend a list of arguments being passed via a template template. The first example works, the second doesn't. The result I'm looking for would be that in the second example, cWapperObject.cMemberVariable.cTuple is of type std::tuple<double, float, short, int>. How can I achieve this? (And please, please do not mention the library boost in the comments or answers).
Example 1:
#include <tuple>
template<class ... TS> class CObject {
std::tuple<TS ...> cTuple;
};
template<template<typename ... TS> class TMPL_CLASS_T>
class CWrapperObject {
TMPL_CLASS_T<double, float, short> cMemberVariable;
};
int main() {
CWrapperObject<CObject> cWapperObject;
return 0;
}
Example 2:
#include <tuple>
template<class ... TS> class CObject {
std::tuple<TS ...> cTuple;
};
template<template<typename ... TS> class TMPL_CLASS_T>
class CWrapperObject {
TMPL_CLASS_T<double, float, TS ...> cMemberVariable;
};
int main() {
CWrapperObject<CObject<short, int>> cWapperObject;
return 0;
}
CObject is a class template, it can be used as a template-template argument. CObject<short, int> is a type, it can't be used as a template-template argument, neither a template-template parameter itself carries any actual type template arguments. You can still deduce the class template name and its parameters using a partial specialization, utilizing a template-template parameter, along with actual type template arguments that were used to instantiate the template:
template <typename... TS>
struct CObject
{
std::tuple<TS...> cTuple;
};
template <typename>
struct CWrapperObject;
template <template <typename...> class TMPL_CLASS_T, typename... TS>
struct CWrapperObject<TMPL_CLASS_T<TS...>>
{
TMPL_CLASS_T<double, float, TS...> cMemberVariable;
};
DEMO

Find template type of a template type c++

I would like to have a function that can take many different things (for simplicity) like so:
template <typename T>
typename type_to_return<T>::type // <-- Use type_to_return to get result type
foo(T t)
{
return typename type_to_return<T>::type(T); // <-- Build a thing!
}
I would then specialize the type_to_return class for the types I have created. This would make the entry be one function and I could then just define new type_to_returns and constructors.
I want type_to_return<T>::type to be just T if T is not some class template. Otherwise I want it to be that class's first template parameter. So for int, I get back int, and for MultOp<float,int,double>, I want to get back float.
How do I do that? I think I need to do something like:
// Base version
template <typename T>
struct type_to_return
{
typedef T type;
};
// Specialized type
template <template <typename> class T>
struct type_to_return<T <any_type_somehow> >
{
typedef template boost::magic_type_unwrapper<T>::param<1>::type type;
};
You may implement a type_unwrapper as follow:
template <typename T>
struct type_unwrapper;
template <template <typename...> class C, typename... Ts>
struct type_unwrapper<C<Ts...>>
{
static constexpr std::size_t type_count = sizeof...(Ts);
template <std::size_t N>
using param_t = typename std::tuple_element<N, std::tuple<Ts...>>::type;
};
which works as long there is no template value as in std::array<T, N>.
Note also that stl container declare some typedef to retrieve there template arguments as std::vector<T, Alloc>::value_type which is T.

partial class template specialization with multiple arguments

I have a template class Foo that takes two (or more) template arguments. I want to use its type in a separate class Bar. See the following simple example, which compiles without error:
template <typename T, typename U> class Foo { };
template <typename T, typename U> class Bar { };
int main()
{
Bar<int, char> bar; // quick example -- int & char could be any 2 types
return 0;
}
The above is somewhat tedious, especially if Foo takes many template arguments and the programmer has to retype them all. I would like to have something like the following instead, but it does not compile:
template <typename T, typename U> class Foo { };
template <typename T, typename U> class Bar; // base
template <typename T, typename U> class Bar< Foo<T, U> > { }; // specialization
int main()
{
typedef Foo<int, char> FooType;
Bar<FooType> bar;
return 0;
}
test.cpp:3:60: error: wrong number of template arguments (1, should be 2)
test.cpp:2:45: error: provided for ‘template class Bar’
test.cpp: In function ‘int main()’:
test.cpp:7:18: error: wrong number of template arguments (1, should be 2)
test.cpp:2:45: error: provided for ‘template class Bar’
test.cpp:7:23: error: invalid type in declaration before ‘;’ token
I am especially perplexed because this partial specialization idiom works fine for a single template argument; see the question titled: total class specialization for a template
Edit I realized that, at least for my purposes, I could get around this using C++11 variadic templates as follows. I still want to know why the second example doesn't work, though.
template <typename... FooTypes> class Bar;
template <typename... FooTypes> class Bar< Foo<FooTypes...> > { };
Your class template Bar<T, U> takes two template arguments, but your specialization is only given one:
template <typename T, typename U> class Bar<Foo<T, U> > {};
Did you mean to have Bar take just one template argument and specialize it correspondingly?
template <typename T> class Bar;
template <typename T, typename U> class Bar<Foo<T, U> > {};
Note that a specialization can depend on a different number of template parameters but the specialization needs to get the same number of arguments. It also works the other way around: a full specialization can have no template parameter:
template <> class Bar<int> {};
I'm a little confused about what you're trying to do in this line:
template <typename T, typename U> class Bar< Foo<T, U> > { }; // specialization
You're stating that the template requires two types, T and U, as type parameters. Foo is itself only one type though, the type created by instantiating the Foo template with those two arguments.
I see that you're expecting it to pick up and determine the T and U since you used them in both places, but that doesn't circumvent the fact that you only provided one type argument for a two type template specialization.

C++ template partial specialization

I cant figure out how to specialize partially this template. compiler complains that template parameter N is not used in partial specialization
#include <boost/multi_array.hpp>
template<typename T, class A>
struct adaptable;
template<typename T, size_t N>
struct adaptable<T,
// line below is the problem
typename boost::multi_array<T,N>::template array_view<2>::type>
{
typedef typename boost::multi_array<T,N>::template array_view<2>::type type;
};
I can add dummy template parameter just to silence compiler.
template<typename T, class A, class A0 = A>
struct adaptable;
template<typename T, size_t N>
struct adaptable<T,
typename boost::multi_array<T,N>::template array_view<2>::type,
boost::multi_array<T,N> >
{
typedef typename boost::multi_array<T,N>::template array_view<2>::type type;
};
is there more straightforward way?
I don't see anything in your example that looks like partial specialization. A partial specialization is a specialization that specifies exact types for some if the base template parameters, but leaves others open. For example:
template <class T, class U>
struct my_template {
// the base template where both T and U are generic
};
template <class T>
struct my_template<int> {
// A partial specialization where T is still generic, but U == int
};
To support partial specialization, the base template has to have at least two template parameters (call the number N). The partially specialized template can have 1..N-1 template parameters. The partial specialization must be located where the compiler will already have "seen" the base template before attempting to compile the partial specialization. The partial specialization is written as a completely separate template from the base template (though the base template and all specializations must have the same name, of course).