Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Partial template-template based specialization vs explicit partial template specialization

Given a template, for example foo:

template<typename... ARGS>
struct foo {};

And two partial specializations for a template bar:

template<template<typename...> class T , typename... ARGS>
struct bar<T<ARGS...>>
{};

template<typename... ARGS>
struct bar<foo<ARGS...>>
{};

Is not the second partial specialization more specialized than the first and must be instanced instead of the template-template parameter specialization?

Some context:

I'm currently writting multiple-variable lambda expressions for template meta-programming based on this paper.

As the paper shows, one could develop easily tmp lambda expressions given a Haskell-like let expression. In my case, I have extended the contents of the paper developing variadic-templates based multiple variable let expressions (Via curryfying multiple nested unary let expressions), and then implementing multiple-variable lambda expressions.

My lambda expression template, tml::multi_lambda, is defined as follows:

template<typename BODY , typename... VARIABLES>
struct multi_lambda
{
    template<typename... ARGS>
    using result = tml::eval<tml::multi_let<VARIABLES...,
                                            ARGS...,
                                            BODY
                                           >>;
};

where tml::eval is a metafunction for evaluating expressions, like Boost::mpl mpl::apply (See my previous question for more context).

The evaluation function tml::eval is specialized both for generic functional expressions, and specifically for this lambda expressions. That are the two specializations of the above example.

When I try to evaluate a lambda expressions, like:

using lambda = tml::multi_lambda<_1,_2, f<_1,_2>>; //f is a function, 
                                                   //_1 _2 are placeholders
using result = tml::eval<lambda,int,int>; //Evaluate lambda with int int as parameters

tml::eval instantiates the generic template-template specialization (Designed for generic evaluable expressions) instead of the partial specialization for lambdas.

EDIT: Implementation of tml::eval and SSCCE

tml::eval is a metafunction dessigned to evaluate any kind of expression, returning the result. The default implementation is specialized for three cases:

  1. The expression is not a function, is a value only: The result of evaluating such expression is the expression itself:

    template<typename E>
    struct evaluate_impl<E>
    {
        using result = E;
    }; 
    
  2. The expression is a function: The result of the evaluation is the value of the result member type of the function. The parameters of the function are evaluated too (To take care of nested expressions):

    template<template<typename...> class F , typename... ARGS>
    struct evaluate_impl<F<ARGS...>> : public F<tml::eval<ARGS>...>
    {};
    
  3. The expression is a function, and more argumments are passed to tml::eval to evaluate the expression with that custom argumments: The parameters of the expression are ignored and the custom are passed and evaluated:

    template<template<typename...> class F , typename... PLACEHOLDERS , typename... ARGS>
    struct evaluate_impl<F<PLACEHOLDERS...>,ARGS...> : public F<tml::eval<ARGS>...>
    {};
    

So tml::eval is just a template alias to ride over the typename ::result:

template<typename... ARGS>
using eval = typename eval_impl<ARGS...>::result;

Finally, the user could specialize eval_impl to override that default behaviour, or make corner cases work. For example, my one-variable lambda expressions template, defined as follows:

template<typename VARIABLE , typename VALUE , typename BODY>
struct lambda
{
    template<typename ARG>
    using result = tml::eval<tml::let<VARIABLE , ARG , BODY>>;
};

specializes eval_impl to make evaluation of lambda expressions work:

template<typename VARIABLE , typename BODY , typename ARG>
struct evaluate_impl<tml::lambda<VARIABLE,BODY>,ARG>
{
    using result = typename tml::lambda<VARIABLE,BODY>::template result<ARG>;
};

The multiple-variable lambda expressions which I have the problem take a similar approach:

template<typename... VARIABLES , typename BODY , typename... ARG>
struct evaluate_impl<tml::multi_lambda<BODY,VARIABLES...>,ARGS...>
{
    using result = typename tml::multi_lambda<BODY,VARIABLES...>::template result<ARGS...>;
};

But instead of working (Like the one variable counterpart), tml::eval instantiates the case three of the default evaluate_impl implementation, or fails due to ambiguous specializations (Case three vs multi_lambda specialization).

Here is an SSCCE:

//An example function:
template<typename... ARGS>
struct F
{
    using result = std::integral_constant<std::size_t,sizeof...(ARGS)>;
};


//This works fine:

using lambda_1 = tml::lambda<_1,F<_1,_1,_1,_1>>;
using result_1 = tml::eval<lambda_1,int>; //Call the lambda with int as parameter


//This doesn't work:

using lambda_2 = tml::multi_lambda<_1,_2,F<_1,_1,_2,_2>>;
using result_2 = tml::eval<lambda_2,int,int>; //Call the lambda with two int as parameters.

The evaluation of lambda_2 fails with:

functional.hpp:167:76: error: ambiguous class template instantiation for 'struct tml::impl::evaluate_impl<tml::impl::multi_lambda<tml::placeholders::_1, tml::placeholders::_2, f<tml::placeholders::_1, tml::placeholders::_1, tml::placeholders::_2, tml::placeholders::_2> >, int, int>' using eval = typename impl::evaluate_impl<EXPRESSION , ARGS...>::result; ^ functional.hpp:116:16: error: candidates are: struct tml::impl::evaluate_impl<F<PLACEHOLDERS ...>, ARG, ARGS ...> struct evaluate_impl<F<PLACEHOLDERS...> , ARG , ARGS...> : ^ In file included from main.cpp:24:0: lambda.hpp:160:16: error: struct tml::impl::evaluate_impl<tml::impl::multi_lambda<BODY, VARIABLES ...>, ARGS ...> struct evaluate_impl<multi_lambda<BODY,VARIABLES...>,ARGS...> :

I'm using GCC4.8.2

like image 245
Manu343726 Avatar asked Nov 10 '22 08:11

Manu343726


1 Answers

First of all, you should really learn what an SSCCE really is, especially the "complete" part. Also, short. That said, I tried to create an SSCCE which seems to reproduce your problem, see at the end of my answer. Looking at the error message you receive, it seems that your real code for the third specialization looks more like

template<template<typename...> class F ,
         typename... PLACEHOLDERS ,
         typename ARG ,
         typename... ARGS>
struct evaluate_impl<F<PLACEHOLDERS...>,ARG,ARGS...>
    : public F<tml::eval<ARG,ARGS>...>
{};

Note the additional explicit mentioning of ARG, which seems superfluous and which could cause the ambiguity in your case. If you replace it with

template<template<typename...> class F ,
         typename... PLACEHOLDERS ,
         typename... ARGS>
struct evaluate_impl<F<PLACEHOLDERS...>,ARGS...>
    : public F<tml::eval<ARGS>...>
{};

the problem might just disappear.

And finally, here's an SSCCE that I used to get a similar error than you.


Update: With your SSCCE from the comments below the situation can be resolved simply by disabling the specialization for F when it is foo. The condition would look like this:

typename std::enable_if<!std::is_same<F<>,foo<>>::value>::type

or see the complete live example based on your SSCCE. By that, you can probably also add ARG back as both specializations should be mutually exclusive now.

like image 161
Daniel Frey Avatar answered Nov 15 '22 08:11

Daniel Frey