How Is P0522R0 Breaking Code

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



Leave a reply



Submit