atomgalaxy / isocpp-universal-template-param

We propose a way to spell a universal template parameter kind. This would allow for a generic apply and other higher-order template metafunctions, and certain typetraits.
https://atomgalaxy.github.io/isocpp-universal-template-param/d1985r0.pdf
2 stars 2 forks source link

Show double template argument list situation #40

Open BengtGustafsson opened 1 year ago

BengtGustafsson commented 1 year ago

This text mainly tries to check what happens when a UA template resolves to a template. I think we need to have some example of this in the proposal. I'm sure someone can write a terser description of the situation.

When universal alias templates, as exemplified by the map_reduce_best metafunction was introduced we got into a situation when a specialization of a template can in itself be a template. To use that second template a second template argument list is needed. I don't think we have a parsing problem there, but it sure looks weird. With longer chains, I assume there is no limit on how many template argument lists you can apply. There is nothing strange with this, its just like functions returning function pointers which you can apply the call operator on.

template<template<template auto...> typename X> template auto tpl = X;

tpl<vector><int> x;

With the new rule that UAs are not dependent if no part of its initializer is dependent (Issue #37) this should be fine and of course the same as vector<int>. If vector is replaced by a dependent name this doesn't matter as the template parameter to tpl must be a template, and as the kind check is deferred we don't have to disambiguate its argument as template either:

template<typename T> void f()
{
    tpl<T::name><int> x;
}

However, if the tpl above is replaced by a dependent name designating a template we must disambiguate both it and what it resolves to, and in case it resolves to a class template we have to disambiguate its result too. We still don't have to disambiguate the template arguments though.


template<typename T> void g()
{
    typename template template T::tpl<T::name><int> x;
}

// Specializable with something like:
struct SPL {
    template auto tpl = ::tpl;
    template auto name = std::vector;
};

T::tpl is disambiguated as a template. Then <T::name> is applied. The result of this must, as T::tplhas unknown kind, be disambiguated if it is not a value. In this case we have to disambiguate it as a template to be able to apply the second template argument list. Finally, we have to disambiguate the result of applying this second template argument list as a type to be able to declare x. While daunting to maintain I think this is no problem parsing-wise.

It does bring up the fact that we would benefit from an alias that can only bind to templates. This would make the examples above more readable and maybe reduce the number of disambiguations in total.

A wild idea would be to allow using to bind to types or templates, with dependent names automatically disambiguating to types (as per down with typename). But this does still not convey the only-template nature. At this point using template sprang to mind and a millisecond later I understood that I was wrong: We do have a two keyword combination with specific meaning: using namespace! We then get:

template<template<template auto...> typename X> using template tpl = X;
// or we can simplify this to:
template<template auto X> using template tpl = X;

struct SPL {
    using template tpl = ::tpl;
    using template name = std::vector;
};

As far as I can see this doesn't reduce the amount of disambiguation we need to apply in these examples. So I would be against this as it can be done using template auto and the use cases are very rare.