C++ template's template failed to compile - c++

I've got this test program
#include<iostream>
#include<vector>
using namespace std;
template<template<class> class C, typename T>
void print(const C<T>& c){
for(auto& e : c)cout<<e<<endl;
}
int main(){
vector<int> v;
print(v);
return 0;
}
It fails to compile:
g++ m.cpp -std=c++11
m.cpp: In function ‘int main()’:
m.cpp:11:16: error: no matching function for call to ‘print(std::vector<int>&)’
print(v);
^
m.cpp:6:6: note: candidate: template<template<class> class C, class T> void print(const C<T>&)
void print(const C<T>& c){
^~~~~
m.cpp:6:6: note: template argument deduction/substitution failed:
m.cpp:11:16: note: template parameters of a template template argument are inconsistent with other deduced template arguments
print(v);
^
I changed print() signature from (const C& c) to (C& c), it still fails:
$ g++ m.cpp -std=c++11
m.cpp: In function ‘int main()’:
m.cpp:11:16: error: no matching function for call to ‘print(std::vector<int>&)’
print(v);
^
m.cpp:6:6: note: candidate: template<template<class> class C, class T> void print(C<T>&)
void print(C<T>& c){
^~~~~
m.cpp:6:6: note: template argument deduction/substitution failed:
m.cpp:11:16: note: template parameters of a template template argument are inconsistent with other deduced template arguments
print(v);
^
How to fix it?

Your compilation problem arises because your template template parameter C doesn't match the declaration of std::vector:
template<
class T,
class Allocator = std::allocator<T>
> class vector;
As you can see, std::vector has two template parameters, while your C has only one. But, note as well that the second parameter (class Allocator) has a default type argument. Since C++17, this is well-formed even the way you've written it, as it was added that template template parameters matching doesn't require specifying parameters for ones with default arguments like Allocator. But not all compilers are supporting this modification to the language spec -- see here live how Clang 6.0.0 refuses to compile your original snippet with C++17 enabled. Going for older versions of C++ (or just any version to date of Clang), this snippet is probably what you were aiming for:
template<template<class, class> class C, typename T, typename A>
void print(const C<T, A>& c){
for(auto& e : c)cout<<e<<endl;
}
As here you specify the correct template signature of the type (std::vector) you're later instantiating print() with.
This would work too, regardless of C++17:
template<template<class...> class C, typename T>
void print(const C<T>& c){
for(auto& e : c)cout<<e<<endl;
}
That said, note that, as vector<int> is already a fully instantiated type, this simpler version works just as well in the given scope of your snippet:
template<typename T>
void print(const T& c){
for(auto& e : c)cout<<e<<endl;
}
I changed print() signature from (const C& c) to (C& c), it still
fails:
This is probably the better practice in this case, as you're not modifying c inside of print(). However, this has nothing to do with your error.

std::vector contains two template parameters, while the template template parameter C is declared to contain only one. Anyway, your code would work fine with C++17; since C++17 (CWG 150), the default template arguments are allowed for a template template argument to match a template template parameter with fewer template parameters.
LIVE
Before C++17 you may apply parameter pack.
template<template<class...> class C, typename T>
void print(const C<T>& c){
for(auto& e : c)cout<<e<<endl;
}
LIVE

Related

C++: How to partially deduce template arguments from make_shared

In order to circumvent the restriction on partially supplied explicit template arguments, I embed the struct from which I want to deduce the class template parameters (Internal) into a second struct (Container).
I would like to enable the user of the code to create e.g. shared pointers of the resulting type. By writing my own create function within the struct, this works just fine.
#include <memory>
/// Container that is used in order to partially specify template arguments
template <int A> struct Container {
/// Contained type, of which the template arguments are deduced.
template <int B> struct Internal {
explicit Internal(std::integral_constant<int, B> fu) { (void)fu; }
};
/// Helper function
template <int C>
[[nodiscard]] static auto create(std::integral_constant<int, C> t) noexcept {
return std::make_shared<Container<A>::Internal<C>>(t);
}
};
int main() {
Container<1>::Internal works{std::integral_constant<int, 8>{}};
auto const worksAswell = Container<1>::create(std::integral_constant<int, 8>{});
}
But when I try to use make_shared directly, it fails. I would like to enable the user to use e.g. the std::make_shared function.
int main() {
auto const fails = std::make_shared<Container<1>::Internal>(std::integral_constant<int, 8>{});
}
From what I understand, this fails because I cannot partially specify template arguments, and I am unable to deduce them from the make_shared function if I don't want to specify all template parameters.
main.cc: In function ‘int main()’:
main.cc:21:74: error: no matching function for call to ‘make_shared<1>(std::integral_constant<int, 8>)’
21 | auto const fails = std::make_shared<1>(std::integral_constant<int, 8>{});
| ^
In file included from /usr/include/c++/9.2.0/memory:81,
from /home/juli/main9.cc:1:
/usr/include/c++/9.2.0/bits/shared_ptr.h:714:5: note: candidate: ‘template<class _Tp, class ... _Args> std::shared_ptr<_Tp> std::make_shared(_Args&& ...)’
714 | make_shared(_Args&&... __args)
| ^~~~~~~~~~~
/usr/include/c++/9.2.0/bits/shared_ptr.h:714:5: note: template argument deduction/substitution failed:
Is it possible to enable generator functions like std::make_shared to partially deduce template arguments like that? The entire code can be found here.
If you create your own make_shared that accepts a template template parameter we can use decltype to deduce the resulting type and pass that on to std::make_shared.
#include <memory>
#include <type_traits>
/// Container that is used in order to partially specify template arguments
template <int A> struct Container {
/// Contained type, of which the template arguments are deduced.
template <int B> struct Internal {
explicit Internal(std::integral_constant<int, B> fu) { (void)fu; }
};
};
template <template <int> typename partial, typename... Args>
auto make_shared(Args&&... args) {
using target_type = std::remove_pointer_t<decltype(new partial{std::declval<Args>()...})>;
return std::make_shared<target_type>(std::forward<Args>(args)...);
}
using std::make_shared;
int main() {
auto const fails = make_shared<Container<1>::Internal>(std::integral_constant<int, 8>{});
static_assert(std::is_same_v<const std::shared_ptr<Container<1>::Internal<8>>, decltype(fails)>);
}
The only issue here is that our make_shared needs to know the template signature of the expected target.
On the positive side we can add several overloads for different template signatures and we can use one parameter pack.

Template template argument deduction failure with GCC (works with MSVC)

I have the following reasonably simple function template:
template <class OrderedSetType, template<class> class SupersetType>
OrderedSetType f(const SupersetType<OrderedSetType>& superset)
{
return OrderedSetType();
}
It's called like this:
f(std::vector<std::string>());
And the compiler fails to deduce the template parameter. The diagnostic message isn't particularly helpful:
<source>: In function 'int main()':
<source>:12:33: error: no matching function for call to 'f(std::vector<std::__cxx11::basic_string<char> >)'
f(std::vector<std::string>());
^
<source>:5:16: note: candidate: template<class OrderedSetType, template<class> class SupersetType> OrderedSetType f(const SupersetType<OrderedSetType>&)
OrderedSetType f(const SupersetType<OrderedSetType>& superset)
^
<source>:5:16: note: template argument deduction/substitution failed:
<source>:12:33: note: template parameters of a template template argument are inconsistent with other deduced template arguments
f(std::vector<std::string>());
^
Why does the error occur? Happens with GCC 7.3 with -std=c++14, does not happen with -std=c++17. Which changes in the C++ 17 standard allowed for this code to compile? And can I make it compile for C++14?
Here's the live demo: https://godbolt.org/g/89BTzz
Specifying the template arguments explicitly doesn't help, by the way.
P. S. In the meantime, MSVC has no problems with this piece of code, but clang 5 and 6 cannot compile it even in C++17 mode. So either clang has a bug and fails to compile standard-compliant code, or GCC has a bug and successfully compiles code that it shouldn't (with -std=c++17).
Which changes in the C++ 17 standard allowed for this code to compile?
You're declaring the template template parameter SupersetType contaning only one template parameter, but the template template argument std::vector<std::string> has two, i.e. std::string and the default template argument std::allocator<string>. Before C++17 they don't match and leads to error (then you have to make them match to solve the issue), since C++17 (CWG 150) it's allowed; i.e. the default template arguments are allowed for a template template argument to match a template template parameter with fewer template parameters.
template<class T> class A { /* ... */ };
template<class T, class U = T> class B { /* ... */ };
template <class ...Types> class C { /* ... */ };
template<template<class> class P> class X { /* ... */ };
X<A> xa; // OK
X<B> xb; // OK in C++17 after CWG 150
// Error earlier: not an exact match
X<C> xc; // OK in C++17 after CWG 150
// Error earlier: not an exact match
Try with
template <template <typename...> class SupersetType,
typename FirstT, typename ... OthersTs>
FirstT f (SupersetType<FirstT, OthersTs...> const & superset)
{ return FirstT{}; }
or also
template <template <typename...> class SupersetType, typename FirstT>
FirstT f (SupersetType<FirstT> const & superset)
{ return FirstT{}; }
The problem is that std::vector doesn't accept only a type but two; the second is an allocator with a default value.
So you have to take in count this problem.
Obviously you can write f() with a template-template parameter that accept only two types
template <template <typename, typename> class SupersetType,
typename FirstT, typename SecondT>
FirstT f (SupersetType<FirstT, SecondT> const & superset)
{ return FirstT{}; }
but if you use a template parameter that accept a variadic list of types, you have a more flexible f() (that match more containers)
While this doesn't provide an answer to your problem, it provide an alternative.
Remember that all standard container have a public type named value_type. That means you could easily skip the template template and only have something like
template<typename ContainerT>
typename ContainerT::value_type f(ContainerT const& superset)
{
return typename ContainerT::value_type();
}
As long as your SupersetType follows the standard containers with a value_type member, it should work.

Parameter pack must be at the end of the parameter list... When and why?

I don't get the reason for which a parameter pack must be at the end of the parameter list if the latter is bound to a class, while the constraint is relaxed if the parameter list is part of a member method declaration.
In other terms, this one compiles:
class C {
template<typename T, typename... Args, typename S>
void fn() { }
};
The following one does not:
template<typename T, typename... Args, typename S>
class C { };
Why is the first case considered right and the second one is not?
I mean, if it's legal syntax, shouldn't it be in both the cases?
To be clear, the real problem is that I was defining a class similar to the following one:
template<typename T, typename... Args, typename Allocator>
class C { };
Having the allocator type as the last type would be appreciated, but I can work around it somehow (anyway, if you have a suggestion it's appreciated, maybe yours are far more elegant than mine!!).
That said, I got the error:
parameter pack 'Args' must be at the end of the template parameter list
So, I was just curious to fully understand why it's accepted in some cases, but it is not in some others.
Here is a similar question, but it simply explains how to solve the problem and that was quite clear to me.
It is valid for function templates but only when argument deduction can help the compiler resolve the template parameters, as it stands your function template example is virtually useless because
template<typename T, typename... Args, typename S> void fn() { }
int main() { fn<int, int, int>(); }
test.cpp: In function 'int main()':
test.cpp:2:32: error: no matching function for call to 'fn()'
int main() { fn<int, int, int>(); }
^
test.cpp:1:57: note: candidate: template<class T, class ... Args, class S> void fn()
template<typename T, typename... Args, typename S> void fn() { }
^
test.cpp:1:57: note: template argument deduction/substitution failed:
test.cpp:2:32: note: couldn't deduce template parameter 'S'
int main() { fn<int, int, int>(); }
the compiler has no way of determining which template parameters belong to the parameter pack, and which to S. In fact as #T.C. points out it should actually be a syntax error because a function template defined in this manner cannot ever be instantiated.
A more useful function template would be something like
template<typename T, typename... Args, typename S> void fn(S s) { }
as now the compiler is able to unambiguously match the function parameter s with the template type S, with the side effect that S will always be deduced - all explicit template parameters after the first will belong to Args.
None of this works for (primary) class templates, parameters aren't deduced and it's expressly forbidden:
From draft n4567
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4567.pdf
[temp.param] / 11
[...]If a template-parameter of a primary class template or alias
template is a template parameter pack, it shall be the last
template-parameter.[...]
(if they were deduced it would be ambiguous as in the function template example).
The first one is not right. The compiler is just buggy and failed to diagnose it. [temp.param]/11:
A template parameter pack of a function template shall not be followed
by another template parameter unless that template parameter can be
deduced from the parameter-type-list of the function template or has a
default argument (14.8.2).
If the function type T(Args...) is meaningful to the end-user, one way to fix this would be to use a partial specialization instead:
template<class F, class Alloc> class C; //undefined
template<class T, class... Args, class Alloc>
class C<T(Args...), Alloc> {
// implementation
};
Depending on the actual requirements, type-erasing the allocator might also be worth considering.

GCC claims a friend function to be overloaded, ambiguous call, clang compiles

template <typename T>
class rp {
};
template <template <typename> class P>
struct b {
template <class, template <typename> class FriendP>
friend void f(b<FriendP> from);
};
template <class, template <typename> class P>
void f(b<P> from) {
}
int main() {
b<rp> v;
f<int>(v);
return 0;
}
Clang 3.3 (svn) compiles fine, while GCC 4.8 rejects it:
main.cpp: In function 'int main()':
main.cpp:17:10: error: call of overloaded 'f(b<rp>&)' is ambiguous
f<int>(v);
^
main.cpp:17:10: note: candidates are:
main.cpp:12:6: note: void f(b<P>) [with <template-parameter-1-1> = int; P = rp]
void f(b<P> from) {
^
main.cpp:8:17: note: void f(b<FriendP>) [with <template-parameter-2-1> = int; FriendP = rp; P = rp]
friend void f(b<FriendP> from);
^
I wonder why GCC claims f to be overloaded. So I guess it's a GCC bug.
Which compiler is right?
Friend injection does no longer exist in the c++ standard, see this for informations about this. However, since the friend function declared inside struct b "acts" on a parameter of type "b", the function is found via ADL (argument-dependant lookup). When this happens 2 different functions having the same signature are declared, and the compiler complains.
This is probably what you meant:
template <template <typename> class P>
struct b {
template <class, template <typename> class FriendP>
friend void f(b<FriendP> from){};
};
but don't use this in real code as it is, since "duplicate function" problems can, as you see, easily arise (proper use of namespaces can be of help with this respect).
The code can be tested here
A good reference for the use (as well as a good real-life example of why they are needed) of template friend functions can be found in item 46 of Effective c++

Passing function template specializations to a variadic template function

I have no problem passing the address of a function template specialization to a regular template function:
template <typename T>
void f(T) {}
template <typename A, typename B>
void foo(A, B) {}
int main()
{
foo(&f<int>, &f<float>);
}
However, when I try to pass the same specializations to a variadic template:
template <typename T>
void f(T) {}
template <typename... A>
void bar(A...) {}
int main()
{
bar(&f<int>, &f<float>);
}
I get the following compiler errors with GCC (I tried 4.6.1 and 4.7.0):
test.cpp: In function 'int main()':
test.cpp:9:27: error: no matching function for call to 'bar(<unresolved overloaded function type>, <unresolved overloaded function type>)'
test.cpp:9:27: note: candidate is:
test.cpp:5:6: note: template<class ... A> void bar(A ...)
test.cpp:5:6: note: template argument deduction/substitution failed:
Why am I getting these errors?
Looks like it might be a bug in GCC that is possibly fixed in GCC 4.6.2 (I say possibly because it's not exactly the same, but does involve getting the address of a variadic template function).