Template template argument deduction failure with GCC (works with MSVC) - c++

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.

Related

Issue with template template parameter in function template

I am trying to use a template template parameter and my code will not complile.
template<class DataType, template<class T> class Container>
void foo(Container<DataType> test)
{
}
int main()
{
std::vector<int> testVec{1,2,3,4};
foo<int, std::vector>(testVec);
}
The error message is
"No matching function for call to 'foo'.
Candidate template ignored: invalid explicitly-specified argument for template parameter 'Container'"
Can someone help me out?
Suggestion: try with
// ...............................VVVVVVVVV
template<class DataType, template<class ...> class Container>
void foo(Container<DataType> test)
{
}
The problem is that std::vector accept two type template parameters; the second one with a default value.
If you write
// ...............................VVVVVVV
template<class DataType, template<class T> class Container>
you ask for a template-template parameter that accept a single type template parameter.
So you can solve asking two type template parameters
// ...............................VVVVVVVVVVVV
template<class DataType, template<class, class> class Container>
or, to be more flexible, a variadic list of type template parameters
// ...............................VVVVVVVVV
template<class DataType, template<class ...> class Container>
As pointed by HolyBlackCat (thanks) your code (so a template-template parameter asking for a single type template parameter) should fail before C++17 but should be accepted starting from C++17 (the matching rules are changed).
Unfortunately this works with g++ but not for clang++: the clang++ developer doesn't implement, by default, this new C++17 matching rule.
But HolyBlackCat report that also clang++ accept your code adding a special parameter: -frelaxed-template-template-args.

False clang error when calling a template template method with a template that is in a namespace

I have a template template method which works fine when calling it with a template that is not in a namespace. However, I get a clang error when calling it with a template that is in a namespace. MSVC and gcc compile without problems, but only when I set the standard to C++17.
Here is a minimal example
#include <vector>
template<template<typename> typename Template>
Template<int> foo() {
return {};
}
template <typename T>
using my_vector = std::vector<T>;
int main()
{
foo<my_vector>(); // compiles
foo<std::vector>(); // does not compile in clang or without C++17
}
Here is a live example.
The gcc error without C++17 is:
<source>:14:5: error: no matching function for call to 'foo'
The clang error is:
<source>:14:22: error: no matching function for call to 'foo<template<class _Tp, class _Alloc> class std::vector>()'
<source>:4:15: note: candidate template ignored: invalid explicitly-specified argument for template parameter 'Template'
What changed in C++17 to allow this, and is it a bug that clang produces an error?
Vector is
template<class T, class Allocator=std::allocator<T>>
class vector;
note it takes 2 parameters.
In c++17 templates that take 2 parameters can match template<class>class if the 2nd one is defaulted; in c++14 this was not true.
As for clang in c++17, they found a bug in the standard if you implement this feature: (via #cpplearner in comments above)
(10): Despite being the resolution to a Defect Report, this feature is disabled by default in all language versions, and can be enabled explicitly with the flag -frelaxed-template-template-args in Clang 4 onwards. The change to the standard lacks a corresponding change for template partial ordering, resulting in ambiguity errors for reasonable and previously-valid code. This issue is expected to be rectified soon.
In c++14 this may work:
template<template<class...> class Z>
Z<int> foo() {
return {};
}
as there are rules letting class... match any number of arguments.
What changed in C++17 to allow this, and is it a bug that clang produces an error?
Yes the behavior changed since C++17, and Clang seems not conformance to the standard.
Note that std::vector has 2 template parameters (the 2nd one has default value), while the template template parameter Template expects only one. They don't match.
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.
template<class T> class A { /* ... */ };
template<class T, class U = T> class B { /* ... */ };
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

Is not matching a template<typename...> to template<typename> a defect?

While exploring this answer I discovered that a template that takes a parameter pack will not be accepted by a template that expects template that has a specific number of parameters.
This seems to me that it is a defect since if a template can take any number of parameters, it should be able to map to a specific number. Is there a language lawyer that could explain why this is not allowed?
Here is a simple example:
template <typename...Ts>
using pack = void;
template <template <typename> class>
using accept_template = int;
accept_template<pack> value = 0;
I wouldn't use it in this exact scenario of course. It would be used to pass a template to another template which would use the passed template in some manner. In my answer that I linked, I have stated a workaround, but I still feel that this is a defect.
This restriction was loosened as a result of P0522, which introduces new rules to handle how template template-arguments match template template-parameters. As a result, from the paper:
template<class T, class U = T> class B { /* ... */ };
template <class ... Types> class C { /* ... */ };
template<template<class> class P> class X { /* ... */ };
X<B> xb; // OK, was ill-formed:
// default arguments for the parameters of a template argument are ignored
X<C> xc; // OK, was ill-formed:
// a template parameter pack does not match a template parameter
Your example fails to compile in C++14, but will compile in C++17.

Template parameters not deducible in partial specialization in gcc6, for a case that used to work in gcc5

This code results in an error in gcc6 (but works fine in gcc 4.8, 5.2 and clang 3.6):
template <typename T>
struct outer
{
template <typename U>
struct inner
{
};
};
template <typename T>
struct is_inner_for
{
template <typename Whatever>
struct predicate
{
static constexpr bool value = false;
};
template <typename U>
struct predicate<typename outer<T>::template inner<U>>
{
static constexpr bool value = true;
};
};
static_assert(
is_inner_for<int>::template predicate<
outer<int>::inner<double>
>::value,
"Yay!"
);
The error is:
main.cpp:22:9: error: template parameters not deducible in partial specialization:
struct predicate<typename outer<T>::template inner<U>> : std::true_type
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:22:9: note: 'U'
^~~~~~~~~~~~~
Commandline is:
g++ -std=c++1y -c main.cpp
See godbolt output here.
I have filed a bug report with gcc here: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70141
However it was marked as invalid (I believe wrongly). The outer<T> that is used inside predicate is a concrete type at that point, so it is not a non-deduced context.
Is there anything in the standard that prevents this being valid c++ code?
I suspect this is a bug in gcc 6.0, and an incorrect warning in clang 3.9 (the warning is weird - because the warning implies that the partial specialization would not be chosen, but if it were not chosen, the static assert would trigger).
From [temp.class.spec.match]:
A partial specialization matches a given actual template argument list if the template arguments of the
partial specialization can be deduced from the actual template argument list
Can we deduce U in typename outer<T>::template inner<U> from outer<int>::inner<double>?
From [temp.deduct.type]:
If a template parameter is used only in non-deduced
contexts and is not explicitly specified, template argument deduction fails.
The non-deduced contexts are:
— The nested-name-specifier of a type that was specified using a qualified-id.
— [...]
But the nested-name-specified here is typename outer<T>, which does not contain the type we're trying to deduce. None of the other non-deduced contexts apply. So deduction should succeed here.
Consider the following equivalent situation:
#include <utility>
template <class >
struct outer
{
template <class U> struct inner {};
};
template <class T>
struct bar {
template <class U> std::false_type foo(U const&);
template <class U> std::true_type foo(typename outer<T>::template inner<U> const&);
};
int main() {
static_assert(decltype(bar<int>{}.foo(outer<int>::inner<double>{}))::value, "!");
}
Both gcc 6.0 and clang 3.9 compile this code without warning - but this is the same sort of deduction that would happen in the partial specialization in the original example.

Template template parameters and default arguments

Consider the following code which uses "template template" parameters to instantiate a class template using multiple types:
#include <iostream>
using namespace std;
enum E
{
a = 0,
b = 1
};
template <template <E> class Action, class T>
void do_something(const T& value)
{
typedef Action<a> type1;
typedef Action<b> type2;
}
template <E e, class Enable = void>
class Foo
{
};
int main()
{
do_something<Foo>(int(55));
}
Using an older compiler (GCC 4.1.2), the above code compiles fine. However, using a newer compiler (GCC 4.4.6 or 4.8.1), the following error is produced:
test3.cpp:25:27: error: no matching function for call to ‘do_something(int)’
do_something<Foo>(int(55));
So it looks like GCC can't bind to do_something, because the template template parameters only declare a single parameter (an Enum), but Foo actually takes two template parameters (even though one is default.) I guess GCC 4.1.2 allowed the default parameter to be ignored.
Okay, so if I change the template definition to:
template <template <E, class> class Action, class T>
void do_something(const T& value)
{
typedef Action<a> type1;
typedef Action<b> type2;
}
...then no version of GCC I tested will compile it. They all produce a similar error:
test3.cpp:13: error: wrong number of template arguments (1, should be 2)
test3.cpp:10: error: provided for ‘template<E <anonymous>, class> class Action’
So now, the compiler complains because the expression typedef Action<a> type1 only provides a single template parameter. Apparently, I'm not able to implicitly use the default parameter here.
Is there some way I can use the default parameter of a template in a template template function?
Default arguments are ignored for parameters of template arguments. There's this example in n3337, chapter [temp.arg.template], paragraph 2:
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 { /∗ ... ∗/ };
template<template<class ...> class Q> class Y { /∗ ... ∗/ };
X<A> xa; // OK
X<B> xb; // ill-formed: default arguments for the parameters of a template argument are ignored
X<C> xc; // ill-formed: a template parameter pack does not match a template parameter
Y<A> ya; // OK
Y<B> yb; // OK
Y<C> yc; // OK
Note the comment at X<B> xb; above. I can't find the normative text, I'm afraid.
You can correlate this with functions - default arguments are not a part of a signature, either. The same thing would also happen if you tried to call a function that has a parameter defaulted through a function pointer.
With a using template alias, a new feature in C++11, you can create a one-parameter template that is equivalent to another template that has two parameters, one of which is defaulted.
template <E e> using Foo1 = Foo<e>;
This creates Foo1, a one-parameter template, even though Foo technically has two arguments, one of which is defaulted. You can use it as:
do_something<Foo1>(int(55));
Alternatively, if C++11 features such as using are not available, then you scan specify the default in your declaration of do_something. This means then, unfortunately, that do_something can no longer deal with simple one-arg templates. Hence, I think the using method above is better.
template <template <E, class = void> class Action, class T>
void do_something(const T& value);
If you take this approach, putting the default in the args to do_something, then this default takes precedence over the default specified at the declaration Foo. This is based on my experiments, and I can't comment with confidence on what is, and is not, standard. But I do think the using trick is fully standards-compliant regarding C++11.
(Ubuntu clang version 3.0-6ubuntu3)