C++ Logo

std-proposals

Advanced search

Re: Explicit alias template specialization

From: Nicolas Weidmann <n.weidmann_at_[hidden]>
Date: Fri, 19 Nov 2021 19:05:19 +0100
 From all the discussions, it seems that my proposal is nothing more
than this:

template<typename T> using alias = typename dependent<T>::type;

with the inconvinience that when first seeing the generic version of the
alias template, the compiler doesn't know if it will have to deal with:

template<typename> using alias = generic-type<T>

or at some later point:

template<typename> using alias = typename dependent<T>::type;

As we say in French, my proposal seems to be a "fausse bonne idée"...

Looks nice at first, but introduces more issues than it solves.

Thanks a lot for the interesting discussion!

Nicolas


On 11/19/21 18:28, Marcin Jaczewski via Std-Proposals wrote:
> pt., 19 lis 2021 o 17:56 Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]> napisał(a):
>> On Fri, Nov 19, 2021 at 11:36 AM Marcin Jaczewski via Std-Proposals <std-proposals_at_[hidden]> wrote:
>>> pt., 19 lis 2021 o 17:28 Matheus Izvekov via Std-Proposals <std-proposals_at_[hidden]> napisał(a):
>>>> On Fri, Nov 19, 2021 at 5:13 PM Arthur O'Dwyer via Std-Proposals <std-proposals_at_[hidden]> wrote:
>>>>> Yes, they are.
>>>>> Dependent member types are (always?) non-transparent "firewalls" against deduction, but aliases are never(!) firewalls.
>>>> If type aliases could be specialized as he proposed, then type aliases
>>>> specializations with dependent template arguments would indeed become
>>>> `firewalls` in all cases except that they could be matched one to the
>>>> other (as in the example from my previous message `alias<T>` would
>>>> match to `alias<int>` giving `T = int`).
>>>>
>>>> In the same way, it's possible to extend TAD such that dependent
>>>> member types could be matched.
>>>> So for example:
>>>> `P = typename T::foo` and `A = X:: foo` -> `T = X`
>>>> `P = typename foo<T>::bar` and `A = foo<int>::bar` -> `T = int`
>>> You can't go back from `bar` to `foo<T>` as `foo<T>::bar` is turing
>>> complete, you would need to check every possible `T` to find a one
>>> matching `bar`.
>>> Simple example:
>>> `P = typename T::foo` and `A = int` how to find `T` then?
>>
>> To be fair, I assume that Matheus is talking only about the "true names" of the types — like, the ones that contribute to mangling. (Do we have a standardese term for "true names"? I've never been aware of one.)
>>
>> So in that hypothetical (and definitely-never-gonna-happen) world where we could deep-pattern-match into true names:
>>
>> template<class T> struct AA { struct Nested {}; };
>> template<> struct AA<double> { using Nested = AA<char>::Nested; };
>> template<> struct AA<float> { using Nested = float; };
>>
>> template<class T> void a(AA<T>::Nested);
>> void a1() { a(AA<int>::Nested()); } // deduces T=int because the "true name" of the argument type is AA<int>::Nested
>> void a2() { a(AA<double>::Nested()); } // deduces T=char (not double!) because the "true name" of the argument type is AA<char>::Nested
>> void a3() { a(AA<float>::Nested()); } // deduction fails because the "true name" of the argument type is float
>>
> Ok, if we restrict only to "true names" (mangling?) then it will work,
> but how exactly will this be done?
>
> ```
> template<class T> struct AA { using Nested = int; };
> //or
> template<class T> struct AA { /* noting */ };
> //or
> template<class T> struct AA; //nowhere defined
> ```
>
> This will still work for `AA<T>::Nested`? You show that it can work in
> specific cases
> but probably it will have too many corner cases to be easily used in
> generic code.
>
> Another question is what will be difference between "fail to match"
> and "do not match"?
> Currently we have "firewall" that cause simply skip this argument,
> but if we try to match it will fail or be silently ignored?
> Depending on definition it could cause breaking change in some specific cases.
>
>
>
>> And then we'd have:
>>
>> template<class T> void nonportable(std::vector<T>::iterator);
>> void call_nonportable() { std::vector<int> v; nonportable(v.begin()); } // deduction fails today;
>> // in the hypothetical world, deduction probably fails, but it depends on your vendor's implementation details
>>
>> If std::vector<int>::iterator is a typedef for __wrap_iter<int*>, then deduction would still fail; if it's a class type with no intervening aliases at all, then deduction would succeed.
>>
>> –Arthur

Received on 2021-11-19 12:05:25