Search code examples
c++templatesvariadic-templatestemplate-meta-programmingpartial-application

`apply` template compiles in g++ but not in clang++ and vc++


The following code compiles successfully in g++ 7.2.0 (compilation flags are -std=c++14 -Wall -Wextra -Werror -pedantic-errors), but it fails to compile in clang++ 5.0.0 (with the same flags, -std=c++14 -Wall -Wextra -Werror -pedantic-errors) and vc++ 15.4 (compilation flags are /EHsc /Za /std:c++14 /permissive-):

template <template <typename...> class Functor, typename... FixedArguments>
struct apply
{
    template <typename... FreeArguments>
    using type = Functor<FixedArguments..., FreeArguments...>;
};

template <typename, typename>
struct Bar{};

template <template <typename...> class>
struct Foo{};

int main()
{
    (void)Foo<apply<Bar, int, char>::type>{};
}

Which compiler behavior is standard compliant? How such template apply may be changed to be compiled on clang++, too?

clang++ error messages:

5 : <source>:5:15: error: too many template arguments for class template 'Bar'
        using type = Functor<FixedArguments..., FreeArguments...>;
                     ^                          ~~~~~~~~~~~~~~~~~
16 : <source>:16:15: note: in instantiation of template class 'apply<Bar, int, char>' requested here
    (void)Foo<apply<Bar, int, char>::type>{};
              ^
9 : <source>:9:8: note: template is declared here
struct Bar{};

vc++ error messages:

5 : <source>(5): error C2977: 'Bar': too many template arguments
9 : <source>(9): note: see declaration of 'Bar'
16 : <source>(16): note: see reference to class template instantiation 'apply<Bar,int,char>' being compiled

Solution

  • As @T.C. noted in the comments to the question such code is ill-formed (no diagnostic required).

    C++14 standard, section "Name resolution" [temp.res], paragraph 8:

    If every valid specialization of a variadic template requires an empty template parameter pack, the template is ill-formed, no diagnostic required.

    Latest drafts of the C++ standard, section "Name resolution" [temp.res], paragraph 8.3:

    ...The program is ill-formed, no diagnostic required, if:

    • ...
    • every valid specialization of a variadic template requires an empty template parameter pack...

    Additional information: Core Issue 2067.

    In accordance with the standard requirements such simple workaround can be written:

    template <template <typename...> class Functor, typename... Arguments>
    struct invoke
    {
        using type = Functor<Arguments...>;
    };
    
    template <template <typename...> class Functor, typename... FixedArguments>
    struct apply
    {
        template <typename... FreeArguments>
        using type = typename invoke<Functor, FixedArguments..., FreeArguments...>::type;
    };
    

    Live demo

    Update: As @odinthenerd noted in the comments this workaround uses an additional type which leads to a slower compilation of the program.