5

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

Community
  • 1
  • 1
Manu343726
  • 13,969
  • 4
  • 40
  • 75
  • @DieterLücking writting an explicit name instead of that examples? – Manu343726 Apr 30 '14 at 17:03
  • Do you mean "Is not the first partial specialization more explicit than the second..." ? – Danvil Apr 30 '14 at 17:08
  • @Danvil thanks for the advise. I mean the second (The `foo` one) over the first (The generic template-template parameter `T` one). – Manu343726 Apr 30 '14 at 17:10
  • `lambda` does not look like a template template to me, so why should the first specialization be used? – Danvil Apr 30 '14 at 17:11
  • 1
    I tried [this SSCCE](http://coliru.stacked-crooked.com/a/0a1f4e8c3e7389e6), but it seems to work. Could you please provide an SSCCE which actually shows your problem? – Daniel Frey Apr 30 '14 at 17:12
  • @DanielFrey Ah, you beat my by mere seconds ;) http://coliru.stacked-crooked.com/a/3607596b68ead0b6 – dyp Apr 30 '14 at 17:13
  • The template template version (the first) is more specialized, and will be preferred over the one using `template` (the second). – Danvil Apr 30 '14 at 17:14
  • 1
    @Danvil why do you think that exactly? – Yakk - Adam Nevraumont Apr 30 '14 at 18:25
  • So a first pass reading is that the more specialized one should be used, and that the general rule for "more specialized" is `A<>` is more specialized than `B<>` if every `B<>` can match `A<>`, while not every `A<>` can match `B<>`. This may not be how the low level details work, but it seems to imply that the **intended** behavior is that the second `bar` is "more specialized". I need to make sure that this matches the standard's text, however, as it is just rules of thumb... – Yakk - Adam Nevraumont Apr 30 '14 at 20:24
  • @Yakk DanielFrey I have updated the question with an in depth explanation of my implementation and a full SSCCE. – Manu343726 May 01 '14 at 21:13
  • This example is neither short, self-contained, nor correct. Is it `evaluate_impl` or `eval_impl`? Where are `let` and `multi_let` defined? What about `_1` and `_2`? `lambda` expects 3 parameters but you attempt to instantiate it with 2... – Oktalist May 02 '14 at 21:57

2 Answers2

1

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.

Daniel Frey
  • 55,810
  • 13
  • 122
  • 180
  • Thanks for your effort. You are right, the third specialization really uses an extra arg to reject the no-parameters case. I have omitted that to just simplify the question. This is a huge amount of code, and what I was trying when I posted the question was to describe the actual problem without just copy-paste the entire library. But you are right, I was very wrong when posting a good SSCCE on this question. I had many problems to write a simple working example of this without posting hundreds of lines of template meta programming code :( – Manu343726 May 05 '14 at 20:36
  • Also, your SSCCE is exactly the situation I have. Thanks a lot. – Manu343726 May 05 '14 at 20:37
  • @Manu343726 Can you, as I suggested, remove `ARG` to solve your problem? You can still reject the no-argument case with `static_assert(sizeof...(ARGS)>0,"Need at least one ARGS");` in the struct if you need. Otherwise, please extend my SSCCE to something which shows what you need, so everyone has a fair chance to help you. – Daniel Frey May 05 '14 at 20:42
  • Mmm i could try to do SFINAE based on the length of the parameter packs. Thanks, I will try it tomorrow. I will post comments (and probably an edit on the question) about the conclusions. Thank you. – Manu343726 May 05 '14 at 20:57
  • I have tried and there is instantation ambiguity too :( http://coliru.stacked-crooked.com/a/eea49967264924e3. What I understand is that our common sense says that the explicit specialization is more specialized than the template-template one, but GCC don't think so. I'm going to read the standard in depth, to see if this is a GCC bug or that situation is correct form the standard point of view. – Manu343726 May 06 '14 at 20:19
  • @Manu343726 Updated the answer based on your SSCCE. – Daniel Frey May 06 '14 at 21:14
  • Cool, why I haven't thought about a `has_custom_eval` to resolve the ambiguity before? Thank you for the idea and all your help these days. – Manu343726 May 07 '14 at 05:28
  • What I miss is a note/reference of the standard about the specialization priority. But, well you have achieved the bounty with three days of help and meta stuff ;) – Manu343726 May 07 '14 at 05:30
  • 1
    @Manu343726 Welcome to the wonderful world of §14.8.2.4. Look at 9/10 and also make sure you read [DR1705](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1705), which actually resulted from this [StackOverflow Q/A](http://stackoverflow.com/questions/15702143). Given your question here, we discussed several options of what your actual case could be and each requires a different specific analysis. If you are interested in that, I suggest you settle for one case and ask a new question with the appropriate SSCCE. – Daniel Frey May 07 '14 at 06:14
  • Today I have tested my multi_lambda on LLVM/Clang using the `has_custom_eval` trick and works perfectly. GCC throws lots of errors in the template alias `result` of the lambda (***Argumment 1 of tml::eval is invalid***). I will submit this as a GCC bug. – Manu343726 May 11 '14 at 16:39
0

it looks like in your multi_lambda you're declaring the template with the the variables... at the end.

template<typename BODY , typename... VARIABLES>
struct multi_lambda
{
    //...
};

Maybe in your usage you should use:

using lambda_2 = tml::multi_lambda<F<_1,_1,_2,_2>,_1,_2>;
phillip voyle
  • 1,863
  • 2
  • 15
  • 18