Template template parameters and weird errors

I'm trying to make a template function that can work with std::unique_ptr, std::shared_ptr, or any other kind of templated smart pointer, as well as a class (WrapperHelper) that does the same thing.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <memory>

template<typename T>
struct Helper
{
	template<template<typename...> class Wrapper = std::unique_ptr, typename... Args>
	static Wrapper<T, Args...> Wrap(T const &t)
	{
		return Wrapper<T, Args...>{new T{t}};
	}
};

template<typename T, template<typename...> typename Wrapper = std::unique_ptr, typename... Args>
struct WrapperHelper final
{
};

int main()
{
	int x = 0;
	auto c1 = Helper<int>::Wrap<>(x);
	auto c2 = Helper<int>::Wrap<std::shared_ptr>(x);
	auto c3 = Helper<int>::Wrap<WrapperHelper, int>(*c1);
}
The compiler errors I am getting are confusing and unhelpful.

GCC: https://ideone.com/gdAQ6M
prog.cpp: In function 'int main()':
prog.cpp:22:48: error: no matching function for call to 'Helper<int>::Wrap(int&)'
  auto c2 = Helper<int>::Wrap<std::shared_ptr>(x);
                                                ^
prog.cpp:7:29: note: candidate: template<template<class ...> class typedef Wrapper Wrapper, class ... Args> static Wrapper<T, Args ...> Helper<T>::Wrap(const T&) [with Wrapper = Wrapper; Args = {Args ...}; T = int]
  static Wrapper<T, Args...> Wrap(T const &t)
                             ^
prog.cpp:7:29: note:   template argument deduction/substitution failed:
prog.cpp: In substitution of 'template<template<class ...> class typedef Wrapper Wrapper, class ... Args> static Wrapper<T, Args ...> Helper<T>::Wrap(const T&) [with typedef Wrapper Wrapper = std::shared_ptr; Args = {}]':
prog.cpp:22:48:   required from here
prog.cpp:7:29: error: wrong number of template arguments (2, should be 1)
In file included from /usr/include/c++/5/bits/shared_ptr.h:52:0,
                 from /usr/include/c++/5/memory:82,
                 from prog.cpp:1:
/usr/include/c++/5/bits/shared_ptr_base.h:345:11: note: provided for 'template<class _Tp> class std::shared_ptr'
     class shared_ptr;
           ^
prog.cpp:23:53: error: no matching function for call to 'Helper<int>::Wrap(int&)'
  auto c3 = Helper<int>::Wrap<WrapperHelper, int>(*c1);
                                                     ^
prog.cpp:7:29: note: candidate: template<template<class ...> class typedef Wrapper Wrapper, class ... Args> static Wrapper<T, Args ...> Helper<T>::Wrap(const T&) [with Wrapper = Wrapper; Args = {Args ...}; T = int]
  static Wrapper<T, Args...> Wrap(T const &t)
                             ^
prog.cpp:7:29: note:   template argument deduction/substitution failed:
Yeah, GCC literally just cuts off at the end.

clang: http://coliru.stacked-crooked.com/a/09d908253332c8b4
main.cpp:22:12: error: no matching function for call to 'Wrap'
        auto c2 = Helper<int>::Wrap<std::shared_ptr>(x);
                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:7:29: note: candidate template ignored: substitution failure [with Wrapper = std::shared_ptr]: too many template arguments for class template 'shared_ptr'
        static Wrapper<T, Args...> Wrap(T const &t)
               ~~~~~~~             ^
main.cpp:23:12: error: no matching function for call to 'Wrap'
        auto c3 = Helper<int>::Wrap<WrapperHelper, int>(*c1);
                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:7:29: note: candidate template ignored: invalid explicitly-specified argument for template parameter 'Wrapper'
        static Wrapper<T, Args...> Wrap(T const &t)
                                   ^
2 errors generated.
Better, but I still have no idea what the problem is.

So, the code works fine with std::unique_ptr but for some reason it thinks I'm trying to pass two template parameters to std::shared_ptr - I can't figure that one out. Then with WrapperHelper I think the template template parameter might be throwing the compilers off, but I don't know how to fix that.

Any ideas?
Last edited on
I wasn't able to pinpoint the problem, but changing return type to static auto Wrap(T const &t) solves 1st problem.

With WrapperHelper you are trying to instantiate WrapperHelper<int, int> which probably is not allowed.
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <memory>

template < typename T > struct helper
{
    template< template<typename...> class WRAPPER, typename... ARGS >
    struct wrapper_type { using type = WRAPPER<ARGS...> ; } ; // *** magic ***

    template < template<typename...> class wrapper = std::unique_ptr, typename... ARGS >
    static typename wrapper_type<wrapper,T,ARGS...>::type wrap( const T& t )
    { return typename wrapper_type<wrapper,T,ARGS...>::type( new T{t} ) ; }
};

////////////////////////// probably simpler ////////////////////////
#include <type_traits>

template < typename T, typename PTR = std::unique_ptr< typename std::decay<T>::type >, typename U >
PTR wrap( U&& v ) { return PTR( new typename std::decay<T>::type( std::forward<U>(v) ) ) ; }
////////////////////////////////////////////////////////////////////

int main()
{
    int x = 7 ;

    auto c1 = helper<int>::wrap<>(x);
    static_assert( std::is_same< decltype(c1), std::unique_ptr<int> >::value, "unexpected" ) ;

    auto c2 = helper<double>::wrap<std::shared_ptr>(x);
    static_assert( std::is_same< decltype(c2), std::shared_ptr<double> >::value, "unexpected" ) ;


    //////////// probably simpler /////////////////
    auto s1 = wrap<int>(x) ;
    static_assert( std::is_same< decltype(s1), std::unique_ptr<int> >::value, "unexpected" ) ;

    auto s2 = wrap< double, std::shared_ptr<double> >(x) ;
    static_assert( std::is_same< decltype(s2), std::shared_ptr<double> >::value, "unexpected" ) ;

    auto s3 = wrap< int, int* >(x) ;
    static_assert( std::is_same< decltype(s3), int* >::value, "unexpected" ) ;
    /////////////////////////////////////////////////
}

http://coliru.stacked-crooked.com/a/19bcc2f344bb0fd1

Bombs with legacy msc++ (18.04) :http://rextester.com/YNOA38814
Compiles cleanly with current msc++ (19.00)
Last edited on
MiiNiPaa wrote:
With WrapperHelper you are trying to instantiate WrapperHelper<int, int> which probably is not allowed.
D'oh, not sure what I was thinking.

@JLBorges: thanks! Could you explain the "magic" though?
Last edited on
When the declaration of the function is parsed (for the first time, before any instantiation), the template parameter pack is used in an un-deduced context.
1
2
3
template < typename T > struct A {} ;

template < typename... TYPES > A< int, TYPES... > foo() ; 

http://coliru.stacked-crooked.com/a/2b122409cc4123af

A pack expansion consists of a pattern and an ellipsis, the instantiation of which produces zero or more instantiations of the pattern in a list (described below). The form of the pattern depends on the context in which the expansion occurs. Pack expansions can occur in the following contexts:
... <elided>...
- In a template parameter pack that is a pack expansion:

if the template parameter pack is a parameter-declaration;
the pattern is the parameter-declaration without the ellipsis;

if the template parameter pack is a type-parameter with a template-parameter-list;
the pattern is the corresponding type-parameter without the ellipsis.
... <elided>...

And:
For the purpose of determining whether a parameter pack satisfies a rule regarding entities other than parameter packs, the parameter pack is considered to be the entity that would result from an instantiation of the pattern in which it appears.

Ergo:
too many template arguments for class template 'A'
template < typename... TYPES > A< int, TYPES... > foo() ; 

error: wrong number of template arguments (2, should be 1)
 template < typename... TYPES > A< int, TYPES... > foo() ; 


Now, it is easy to see why the "magic" gets rid of the 'wrong number of template arguments' error when the declaration is parsed. (We would still get the error if we try to instantiate it wrongly.)

auto as the result type (without a trailing type-specifier) would also get rid of the error if the function is defined inline and the type involved is the result type.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
template < typename T > struct A {} ;

template< template<typename...> class X, typename... ARGS >
struct pack_types { using type = X<ARGS...> ; } ;

template < typename... TYPES > typename pack_types< A, int, TYPES... >::type foo() ; 

#include <type_traits>

int main()
{
    decltype( foo<>() ) a ;
    static_assert( std::is_same< decltype(a), A<int> >::value, "unexpected" ) ;
    
    // decltype( foo<double>() ) b ; // *** error: wrong number of template arguments (2, should be 1)
}

http://coliru.stacked-crooked.com/a/ce31c2ac7e662c00
Last edited on
Thanks!
Topic archived. No new replies allowed.