5

This code:

#include <memory>

template <template <typename> class Ptr>
class A { Ptr<int> ints; };

using B = A<std::unique_ptr>;

yields the following error (with GCC 6.3):

a.cpp:6:28: error: type/value mismatch at argument 1 in template parameter list for ‘template<template<class> class Ptr> class A’
 using B = A<std::unique_ptr>;
                            ^
a.cpp:6:28: note:   expected a template of type ‘template<class> class Ptr’, got ‘template<class _Tp, class _Dp> class std::unique_ptr’

Now, I can work around this, like so:

template <typename T>
using plugged_unique_ptr = std::unique_ptr<T>;
using B = A<plugged_unique_ptr>;

but why do I have to? I mean, why isn't the compiler willing to "plug" the second template parameter of std::unique_ptr with its default value and allow std::unique_ptr to be used as a template argument to A?

einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • 1
    Note that it should compile since `C++17`. – HolyBlackCat Jul 16 '18 at 08:43
  • @HolyBlackCat can you point me to the reason why C++17 makes this work? – rubenvb Jul 16 '18 at 08:44
  • @rubenvb [This page](https://en.cppreference.com/w/cpp/language/template_parameters) has some explanations. – HolyBlackCat Jul 16 '18 at 08:47
  • @HolyBlackCat I guess what you meant to say is this bit on that page: "To match a template template argument A to a template template parameter P, each of the template parameters of A must match corresponding template parameters of P exactly (until C++17) P must be at least as specialized as A (since C++17)" – rubenvb Jul 16 '18 at 08:55
  • Possible duplicate of [Template template parameter and default values](https://stackoverflow.com/questions/48645226/template-template-parameter-and-default-values) – Arne Vogel Jul 25 '18 at 11:37

3 Answers3

6

Because template template parameters need to match exactly. This means the default template argument is not relevant here. Note that extending your template template argument to two template arguments will only work by chance: an implementation is permitted to add more template arguments than defined by the standard, and some often do in the case of SFINAE around std containers.

This is also the prime reason I generally advise against using any template template arguments, and instead just use a plain template typename. If you need access to nested template types, provide internal accessors in line of e.g. value_type or external accessors such as tuple_element to access these inside the template.


Note: This apparently has changed in C++17, where the matching is not exact anymore, but slightly relaxed yet more complicated. Nevertheless, I would still advise against using template template parameters in general.

rubenvb
  • 74,642
  • 33
  • 187
  • 332
4

std::unique_ptr has a second template argument with a default so template <typename> class Ptr doesn't match std::unique_ptr

template <typename...> class Ptr will work

cppreference

Tyker
  • 2,971
  • 9
  • 21
1

As @HolyBlackCat suggests, we no longer have to use any workaround with C++17 - and OP's code does indeed compile (coliru.com).

GCC 6.3.0 compiles C++14 code by default and doesn't apply this language semantics change.

einpoklum
  • 118,144
  • 57
  • 340
  • 684