0

I wanted to create a type that holds a generic type type, that, itself, is a template with one argument. So, if my type is called C, it could be summarized like C<T<U>>.

So, I went for it:

#include <vector>

template <template<typename> class T>
class C
{
    // implementation here
};

int main() {
    C<std::vector<int>> myType;
}

And I am facing this error:

<source>:10:7: error: template argument for template template parameter must be a class template or type alias template
    C<std::vector<int>> myType;
      ^
1 error generated.
ASM generation compiler returned: 1

What is the correct way of force a template to take a type that itself, is a template that expects other template argument?

Live example.

Enlico
  • 23,259
  • 6
  • 48
  • 102
Alex Vergara
  • 1,766
  • 1
  • 10
  • 29
  • What’s the point of constraining it to another template? What’s the goal? – Daniel A. White Mar 17 '23 at 12:59
  • 6
    `std::vector` is not a template, it is a concrete type. `std::vector` itself is the template. – NathanOliver Mar 17 '23 at 12:59
  • Do you want just `typename T`? Or do you only want to accept templates for some reason? – HolyBlackCat Mar 17 '23 at 13:00
  • @HolyBlackCat probably I missused the template word there. I want to have a type, that is able to accept C>, being always T a type that must be specified in terms of U. – Alex Vergara Mar 17 '23 at 13:02
  • 1. Do you want to reject `C`? 2. Given `C>`, do you want to know `U`? – HolyBlackCat Mar 17 '23 at 13:06
  • You can declare a template `C` accepting any type, and then define only a partial specialization `C>` (leaving the other cases undefined). I fail to see why this is a good idea, though. What you are actually trying to accomplish here? What's the general picture? Why making `C` accepting all types is not OK? – chi Mar 17 '23 at 13:06
  • Are you saying that you want `C` to only accept a template parameter that is itself an instantiated template? You may need to [edit] this question to clarify what youare trying to achieve. – Drew Dormann Mar 17 '23 at 13:06
  • What's the actual problem you're trying to solve? Do you only want to accept container types? – HolyBlackCat Mar 17 '23 at 13:07
  • @HolyBlackCat probably I am following a convoluted way, but there's some CRTP code behind the scenes, so I wanted to constraint the template. 1.) Yes 2.) Sure, it will be present in a using declaration later – Alex Vergara Mar 17 '23 at 13:09
  • Can you explain more about how you use CRTP? – HolyBlackCat Mar 17 '23 at 13:41
  • @HolyBlackCat better if you read by yourself https://github.com/Pyzyryab/Zero/tree/physics-library/zero/ifc/physics/quantities On the dimensions module is the initial try to be able to set up derived magnitudes, and one approach I am trying is based on CRTP (origin isn't up to date to my last local changes), but i am pretty sure that you will be able to figure it out – Alex Vergara Mar 19 '23 at 14:54

1 Answers1

3

You can specialize the class for the case you need and let it undefined otherwise

#include <vector>

template <typename  T>
class C;

template <template<typename> typename TT, typename U>
class C<TT<U>>
{
    // implementation here
};

int main() {
    C<std::vector<int>> myType;
    // C<int> myTypeWrong; // compile error
}

The above does not work before C++17 because std::vector has a default for its second template type parameter,

template<
    class T,
    class Allocator = std::allocator<T>
> class vector;

and before C++17 the part = std::allocator<T> is ignored, as far as matching the std::vector template template argument with the TT template template parameter above, so std::vector<T, std::allocator<T>> never successfully match C<TT<U>>. (Same thing holds for template<typename, typename = void> struct V {}; as it holds for std::vector, clearly.)

This, together with the workaround mentioned in a comment and based on the usage of a variadic template parameter for the template template parameter, is explained in C++ Templates - The Complete Guide 2nd edition, §12.3.4, page 197-198.

As pointed out in another comment, Clang hasn't adopted the C++17 change to the mentioned part of the standard.

Enlico
  • 23,259
  • 6
  • 48
  • 102
  • 3
    Care with standard though (pre C++17), `std::vector` has also `Allocator`... – Jarod42 Mar 17 '23 at 13:12
  • Also, Clang15 is reporting this: `:13:25: error: implicit instantiation of undefined template 'C>'`. GCC12 compiles fine, btw – Alex Vergara Mar 17 '23 at 13:15
  • @AlexVergara, I think that is related to Jarod42's comment. – Enlico Mar 17 '23 at 13:24
  • @Enlico flags are in C++20 – Alex Vergara Mar 17 '23 at 13:28
  • @AlexVergara, but GCC is not erroring. So one of the two compilers is correct and the other wrong. If clang is wrong, then the things are possibly related and clang is just giving an outdated diagnostic. Since GCC gives in C++14 basically the same error as clang gives in C++14 and in C++17/20, I think it's most likely that GCC is correct. – Enlico Mar 17 '23 at 13:31
  • @Enlico yes, totally agree. Your response is mostly that what I was expecting, but without having to justify for what I need that things that I asked for. Which is always nice, when the question is all about C++ syntax. – Alex Vergara Mar 17 '23 at 13:33
  • @AlexVergara, I don't think the others wanted that you justified anything. It's just that some people (including me, tbh) think that maybe some more information about the use case could unveil that you don't need the thing you asked for. – Enlico Mar 17 '23 at 13:34
  • 2
    Use `template typename` to make it work in Clang. Clang is in the wrong, they refused to support your usage by default (`-frelaxed-template-template-args` enables it). – HolyBlackCat Mar 17 '23 at 13:58
  • @HolyBlackCat I really appreciate your answer. I am still trying to figure out a lot of things about templates, and I usually tend to stick with Clang for... everything. So, thanks for point this – Alex Vergara Mar 19 '23 at 14:57
  • @Enlico My doubt is purelly a syntax thing, even I am not sure if I will implement it in the code base. Since the language allows it, is just nice to know about it. That's why I am being grateful to you for just answer – Alex Vergara Mar 19 '23 at 15:01