Why can't I use std::unique_ptr as a "template<class> class" argument?
Asked Answered
J

3

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?

Jacquiline answered 16/7, 2018 at 8:35 Comment(5)
Note that it should compile since C++17.Subsolar
@Subsolar can you point me to the reason why C++17 makes this work?Jefferyjeffie
@Jefferyjeffie This page has some explanations.Subsolar
@Subsolar 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)"Jefferyjeffie
Possible duplicate of Template template parameter and default valuesRebroadcast
J
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.

Jefferyjeffie answered 16/7, 2018 at 8:43 Comment(4)
Sometimes you need them, if you want to apply multiple inner-template arguments.Jacquiline
@Jacquiline I'm sure sometimes you need them, but I'm unsure what "applying multiple inner-template arguments" means. Could you give an example?Jefferyjeffie
template <template <typename> class Ptr> struct buffers_t { Ptr<foo[]> foos; Ptr<bar[]> bars; Ptr<baz[]> bazes; }Jacquiline
@Jacquiline How the obvious can elide you. Thanks for taking the time :).Jefferyjeffie
A
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

Apis answered 16/7, 2018 at 8:39 Comment(2)
Doesn't quite answer my question, but it's a clever workaround, so +1!Jacquiline
I'd be tempted to template<template<typename, typename...> class Ptr> to be explicit about the number of expected parametersFootsore
J
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.

Jacquiline answered 16/7, 2018 at 9:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.