How is P0522R0 breaking code?
You can have code like this:
template<template<typename> typename>
struct Foo {};
template<typename, typename = void>
struct Bar {};
Foo<Bar> unused;
Without the defect resolution, unused
would be ill-formed, because foo
takes a template with only one template parameter, and not two. If you relied on this (maybe for SFINAE):
template<template<typename> typename>
void foo();
template<template<typename, typename> typename>
void foo();
template<typename, typename = void>
struct Bar {};
int main() {
foo<Bar>(); // ambiguous after resolution!
}
Then the call would fail! The problem is that there was no corresponding change to partial ordering, and so both candidate functions have the same viability, and the call is ambiguous.
Deducing first template argument with other template parameters defaulted
This is perfectly valid code, and gcc is right. The "feature" was introduced in C++17. It's not really a feature because it is a defect report. MyDelegate
matches the partial specialization of signature_traits
, and so it should be taken as gcc correctly does. Note that it works because the second template parameter is defaulted.
The reason why clang doesn't compile it is because that defect report has a defect :P. It doesn't introduce the appropriate change in partial ordering, which is not really nice and makes previousy valid code ambiguous again.
It is expected to be fixed soon, but in the meanwhile, clang decided to "hide" the feature behind a flag, -frelaxed-template-template-args.
So, just compile with that flag enabled and you should be fine.
Substitution failure for template template argument
Suggestion: try with
#include <vector>
template <template <typename...> typename Container, typename ... Ts>
struct SomeClass {
SomeClass(const Container<Ts...>& c) {
}
};
template <template <typename...> typename C, typename ... Ts>
inline auto make_some_class(const C<Ts...>& container) {
return SomeClass<C, Ts...>(container);
}
int main() {
std::vector<int> ints;
auto stuff = make_some_class(ints);
}
I mean... I suppose the problem is that std::vector
isn't a container that receive one type template parameter; it's a container that receive two type template parameter (the second one with a default type: std::allocator<T>
where T
is the first one).
So the suggestion is: make SomeClass
more flexible and able to receive a container with an variadic list of template types of arguments
template <typename...> typename Container
and the corresponding list of template types
typename ... Ts
If you want a variadic list of arguments (Ts...
) you need it in last position so you have to switch the position of Container
and T
(now Ts...
): before Container
and after the variadic list Ts...
template <template <typename...> typename Container, typename ... Ts>
struct SomeClass {
SomeClass(const Container<Ts...>& c) {
}
};
Not strictly required but, for uniformity, I suggest to rewrite make_some_class()
in the same way (and, obviously, pass C
before Ts...
in the template parameters list).
template <template <typename...> typename C, typename ... Ts>
inline auto make_some_class(const C<Ts...>& container) {
return SomeClass<C, Ts...>(container);
}
C++ function for container
Don't overthink it.
template <typename Container>
void printIntegers(const Container& container)
{
static_assert(std::is_same_v<typename Container::value_type, int>);
for (const auto& el : container)
{
printf("%d ", el);
}
}
Or even just:
template <typename Container>
void printThings(const Container& container)
{
for (const auto& el : container)
{
std::cout << el << ' ';
}
}
Matching template template parameters with default arguments
gcc accepts this since 7.1. clang intentionally does not accept it unless you provide a new flag:
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.
For examples of said breaking code, see this question.
Compilation demo.
Also the latest update on the Core Issue in question is that it was:
[Moved to DR at the November, 2016 meeting as paper P0522R0.]
It's just at the top of the issue instead of the bottom. I added the link.
How to match only variadic templates with a template template parameter?
We can determine whether a class template that can be instantiated with 0 template arguments is genuinely variadic or (merely) has defaults for all its non-variadic template arguments, by counting the arguments to an 0-argument instantiation:
template<class> constexpr unsigned argc_v;
template<template<class...> class C, class... A> constexpr unsigned argc_v<C<A...>> = sizeof...(A);
template<template<class...> class, class = void> constexpr bool is_variadic_v = false;
template<template<class...> class C> constexpr bool is_variadic_v<C, std::void_t<C<>>> = argc_v<C<>> == 0;
Then we can use this to build a set of specializations that respectively accept only variadic, 1-argument (with possible default) and 2-argument (with possible default/s) class templates:
template<template<class...> class, class = std::true_type>
struct foo;
template<template<class...> class C>
struct foo<C, std::bool_constant<is_variadic_v<C>>> {
foo() { std::cout << "variable case\n"; }
};
template<template<class> class C>
struct foo<C, std::bool_constant<!is_variadic_v<C> && argc_v<C<void>> == 1>> {
foo() { std::cout << "single param case\n";}
};
template<template<class, class> class C>
struct foo<C, std::bool_constant<!is_variadic_v<C> && argc_v<C<void, void>> == 2>> {
foo() { std::cout << "two param case\n";}
};
I'm a bit disappointed that the latter argc_v
tests are necessary (in C++20 mode); I think it's something to do with P0522 / CWG150.
Demo.
How to pass the name of a template class to a template argument?
You are looking for a template template parameter:
#include <set>
#include <vector>
template<template<typename> typename CONTAINER>
void f(CONTAINER<int> *){}
int main(){
std::vector<int> v;
f(&v);
std::set<int> s;
f(&s);
}
"Matching template template parameters to compatible arguments" is a C++17 feature. This won't work with C++14 because std::vector
has more than one template parameter.
You need -frelaxed-template-template-args
to compile this code with Clang, see: Template template parameter in function templates and How is P0522R0 breaking code?
An alternative way is to use template template parameters with variadic templates to avoid "Matching template template parameters to compatible arguments":
#include <set>
#include <vector>
template<class T, template<class, class...> class CONTAINER, class... Args>
void f(CONTAINER<T, Args...> *){}
int main(){
std::vector<int> v;
f(&v);
std::set<int> s;
f(&s);
}
or
#include <set>
#include <vector>
template<template<class, class...> class CONTAINER, class... Args>
void f(CONTAINER<int, Args...> *){}
int main(){
std::vector<int> v;
f(&v);
std::set<int> s;
f(&s);
}
This works with C++11.
Related Topics
Is It Legal to Compare Dangling Pointers
Converting String of 1S and 0S into Binary Value
Why Stdfax.H Should Be the First Include on Mfc Applications
How to Properly Use Std::String on Utf-8 in C++
What Is Uint_Fast32_T and Why Should It Be Used Instead of the Regular Int and Uint32_T
How to Scale Down Numbers from Rand()
Search a Vector of Objects by Object Attribute
How to Generate All Permutations of an Array in Sorted Order
"Not Declared in This Scope" Error with Templates and Inheritance
Child Process Receives Parent's Sigint
Blending Does Not Remove Seams in Opencv
Non-Class Rvalues Always Have Cv-Unqualified Types
Template Within Template: Why "'>>' Should Be '> >' Within a Nested Template Argument List"
What Is the Status of N2965 - Std::Bases and Std::Direct_Bases
How to Reset Std::Cin When Using It
Why Not Use Pointers for Everything in C++