# Re: [std-proposals] Function overload set type information loss

From: Jonathan Wakely <cxx_at_[hidden]>
Date: Tue, 30 Jul 2024 08:18:10 +0100
On Tue, 30 Jul 2024, 01:02 organicoman via Std-Proposals, <
std-proposals_at_[hidden]> wrote:

>
> Hello Gents,
>

Not everybody is here is a gent.

Let:
> O(f) be the Set of 'function overloads' of the function named "f".
>
> Let:
> "Ret" be the return type of function "f".
>
> The elements of O(f) can have 2 forms:
>
> 1-> Ret f ( Vargs )
>
> 2-> Ret f <Ts...> ( Fargs )
>
> Where:
> 1-> "Vargs" is a variadic list of arguments types, participating or not
> in template type arguments deduction if "f" is a template. e.g:
> template<typename T, typename V>
> void f(T, V, double, int)
> Vargs = {T, V, double, int}
>
> 2-> "Fargs" is a fixed list of arguments types, not related to any
> template type parameter. e.g:
> template <typename T, typename V>
> void f(double, int)
> Fargs= {double, int} // T,V are not in the list.
>
> "Ts" is just a variadic list of template parameters types.
>
> The type of any function of the 1st form is:
> decltype (f) = Ret(*)(Vargs),
> which keeps information about the function's type template parameters
> participating in the function argument's list.
>
> But the type of the 2nd form is:
> decltype (f) = Ret(*)(Fargs);
> No mater what the template parameters are, the type of the 2nd form always
> decays to:
> Ret(*)(Fargs)
>
> And always lose any type information about the function's type template
> parameters
>
> Yet, when we want to get the address of such function, we are obligated
> to use the template types in the function name. e.g:
> auto select_f = &f<Ts...>;
>
> Otherwise we get overload ambiguity,
> This is a proof that the template arguments participates in the function
> type.
>

No, not part of the type. They're part of the function template
specialization's _signature_, not its type.

> In my opinion, the compiler should keep the template arguments type
> information.
>
> I know that changing the type of "select_f" in the example above will
> break a lot of code.
> But i have a suggestion.
> If the compiler can keep record of :
> * an apparent function type ; (the usual one)
> decltype (&f<Ts...>) = Ret(*)(Fargs)
> * and an effective function type which is:
> decltype (&f<Ts...>) = Ret(*)<Ts...>(Fargs)
>
> This would fix the problem without breaking any neck.
>
> Why is this useful?
> Take this example:
>
> struct Erased{
> std::any (*m_fun) (void);
>
> template<auto Func>
> constexpr Erased()
> : m_fun(Func)
> { }
>
> auto operator ()()
> {
> using f_type = effective_decltype (m_fun);
> // imagin we have a type traits that
> // extracts template types.
> using T = extract_1st_template_type<f_type>;
>
> return std::any_cast<T>( m_fun(void) );
> }
>
> };
>
> template <typename Ret>
> std::any foo()
> { return Ret{}; }
>
> int main()
> {
> std::vector<Erased> vec;
> vec.push_back(Erased<&foo<int>>{});
> vec.push_back(Erased<&foo<double>>{});
> vec.push_back(Erased<&foo<some_type>>{});
> for(const auto& elem: vec)
> DoSomethingBasedOnReturnType(elem());
> }
>
> Using this technique we can store the template type, then recall it back.
> I guess it will make type erasure more effecient.
>
> Any thoughts?
>
> Regards