Search code examples
c++templatesc++17language-lawyertemplate-meta-programming

inconsistent treatment of class template with defaulted argument as template template class argument of smaller arity


The following snippet is rejected by Clang on C++17, accepted by GCC on C++17 and rejected by GCC on C++14. This is consistent with all recent compiler versions. What's the correct behavior ?

template <class T, class = int>
struct test1 {};

template <class T, template <class> class>
struct test2 {};

using test_t = test2<int, test1>;

EDIT: Apparently this is actually implemented on Clang, it just needs an explicit flag: -frelaxed-template-template-args which is not included when using -pedantic.


Solution

  • GCC's is the correct behavior. It is consistent with p0522r0, which was accepted into C++17.

    From the paper's introduction:

    Template template-parameters only bind to arguments with matching parameter lists. Matching is currently defined as an exact match; each template parameter in the argument's parameter list must have the same kind and type as the corresponding parameter in the template-parameter's parameter list. (There is an ill-advised exception to this rule when a parameter pack appears in the template-parameter's parameter list; this paper preserves that special behavior.) The matching rules exclude many reasonable arguments. This paper adjusts the matching rules to invoke partial ordering to determine when a template template-argument is a valid match for a template-parameter.

    The paper also contains code examples of what will change, and this snippet is very much like the example whose behavior you observed:

    template <template <typename> class> void FD();
    template <typename, typename = int> struct SD { /* ... */ };
    FD<SD>();  // OK; error before this paper (CWG 150)