Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 271
einpoklum Avatar asked Jul 16 '18 08:07

einpoklum


3 Answers

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.

like image 94
rubenvb Avatar answered Oct 11 '22 22:10

rubenvb


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

like image 29
Tyker Avatar answered Oct 11 '22 21:10

Tyker


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.

like image 1
einpoklum Avatar answered Oct 11 '22 21:10

einpoklum