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
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.
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
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.
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.
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)