Reputation: 520
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.
Upvotes: 2
Views: 1320
Reputation: 48467
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>>{};
}
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>{};
}
Upvotes: 1
Reputation: 119867
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.
Upvotes: 3