c++g++language-lawyerclang++template-templates

What "conversion" of template template parameters is allowed in C++?


I'm trying to understand under what circumstances I can pass a type template as an argument for a template template parameter with a different signature. E.g., I would expect that the following might compile successfully.

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

template <template <typename> typename Template>
struct bar { };

using quux = bar<foo>;

After all, bar is asking for a template that is willing to accept exactly one type template parameter, and I'm giving it one. But with Clang 11.0.1, this doesn't compile.

OK, fine. Surely if that's disallowed, the following should also be disallowed.

template <typename>
struct foo { };

template <template <typename...> typename Template>
struct bar { };

using quux = bar<foo>;

Here bar asks for a template that can accept any number of type template parameters, and I'm not giving it one. And yet Clang accepts this! (Of course it still fails if I write code inside of bar that instantiates Template with template arguments not matching foo's signature.)

This seems backwards to me. It's allowing conversion of more specific template signatures to more general ones instead of the other way around.

I tried with GCC 10.2.0 too. GCC accepts both. Lol.

I'm compiling with -Wall -Wextra -O0 -std=c++20 -pedantic with both compilers.

Does the standard really not specify whether the first example above is ill-formed, or is at least one of the compilers non-conforming?

And, regardless of what the standard requires, why would Clang make the choices it makes here? It seems like a bug to me.


Solution

  • I think Clang is wrong. Since C++17 (P0522R0, CWG 150), templates taking parameter pack like foo, should be allowed to be specified as template template argument for bar even it expects one template parameter.

    template<class T> class A { /* ... */ };
    template<class T, class U = T> class B { /* ... */ };
    template <class ...Types> class C { /* ... */ };
    
    template<template<class> class P> class X { /* ... */ };
    X<A> xa; // OK
    X<B> xb; // OK in C++17 after CWG 150
             // Error earlier: not an exact match
    X<C> xc; // OK in C++17 after CWG 150
             // Error earlier: not an exact match