Ricardo_arg
Ricardo_arg

Reputation: 520

Variadic template argument within template template parameter in a partial specialization

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

Answers (2)

Piotr Skotnicki
Piotr Skotnicki

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>>{};
}

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

Upvotes: 1

n. m. could be an AI
n. m. could be an AI

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

Related Questions