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

Reversing the decision to make all UAs dependent #37

Open BengtGustafsson opened 1 year ago

BengtGustafsson commented 1 year ago

I think this decision was wrong. It was taken in a haste. My reasoning now revolves around the map_reduce_best metafunction. This is a universal alias template whose kind will be known except in the unlikely case that one of the template arguments is dependent. It seems overly zealous to require disambiguation of the result every time map_reduce_best is called with a Reduce metafunction yielding a type.

Take the reducer std::tuple. With this convenient tuple builder we can for instance create a tuple of references easily:

template<typename... Ts> class S {
    S(Ts... ts) {
        map_reduce_best<std::add_reference_t, std::tuple, Ts...> vs(ts...);
    }
};

Note the definition of map_reduce_best from R3:

template<template<template auto> template auto Map,
         template<template auto...> template auto Reduce,
         template auto... Args>
template auto map_reduce_best = Reduce<Map<Args>...>;

Thanks to co-variant behavior of template template parameters std::add_reference_t substitutes for Map and std::tuple substitutes for Reduce. It seems logical that S::vs can be declared without disambiguating the result of map_reduce_best as typename as there are no dependent names in its template argument list.

BengtGustafsson commented 1 year ago

This offers possibilities to bring templates and concepts into scope without having to repeat their template parameter lists:

template auto MT = ComplicatedNamespaceName::MyTemplate;
template auto DT = DependentClass::template MyTemplate;  // Disambiguate dependent name once and for all!
template auto MC = ComplicatedNamespaceName::MyConcept;
BengtGustafsson commented 1 year ago

Yes, as Gasper noted today it may be to much to ask that the compiler, during parsing, should be able to deduce that given std::tuple for Reduce map_reduce_best will yield a type.

On the other hand, compare this situation:

template<typename T> class S {
    std::vector<std::conditional_t<sizeof(T) == 4, T, T*>> vec;
}:

What's the difference here, actually? We could build arbitrarily complex metafunctions instead of just std::vector<std::conditional<sizeof(T) == 4, T, T*>> and the compiler would still have to figure out that the result is a type as long as there are no dependent names involved.

I don't see why the compiler would not evaluate map_reduce_best at parse time as there are no dependent names involved it will end up with a type as the result, thus not requiring any disambiguation. It is like the corollary of deferred kind checking: If you can kind check you can also kind check the result of the metafunction even if it has UAs or UTPs inside as their actual kind will be known. Yes, this does need some extra compiler logic, but I think it is fairly easy to implement.

atomgalaxy commented 1 year ago

It requires partial const-propagation, in effect.

I'm afraid the compiler will need dependent-dependent names while parsing template bodies :)

On Wed, Sep 28, 2022 at 8:49 PM Bengt Gustafsson @.***> wrote:

Yes, as Gasper noted today it may be to much to ask that the compiler, during parsing, should be able to deduce that given std::tuple for Reduce map_reduce_best will yield a type.

On the other hand, compare this situation:

template class S { std::vector<std::conditional_t<sizeof(T) == 4, T, T*>> vec; }:

What's the difference here, actually? We could build arbitrarily complex metafunctions instead of just std::vector<std::conditional<sizeof(T) == 4, T, T*>> and the compiler would still have to figure out that the result is a type as long as there are no dependent names involved.

I don't see why the compiler would not evaluate map_reduce_best at parse time as there are no dependent names involved it will end up with a type as the result, thus not requiring any disambiguation. It is like the corollary of deferred kind checking: If you can kind check you can also kind check the result of the metafunction even if it has UAs or UTPs inside as their actual kind will be known. Yes, this does need some extra compiler logic, but I think it is fairly easy to implement.

— Reply to this email directly, view it on GitHub https://github.com/atomgalaxy/isocpp-universal-template-param/issues/37#issuecomment-1261394817, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAA5R5L4GROCWP5QSKEWLJLWASON5ANCNFSM6AAAAAAQO7FBBA . You are receiving this because you are subscribed to this thread.Message ID: <atomgalaxy/isocpp-universal-template-param/issues/37/1261394817@ github.com>