C++ Logo

std-proposals

Advanced search

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

From: Jason McKesson <jmckesson_at_[hidden]>
Date: Wed, 31 Jul 2024 18:27:36 -0400
On Wed, Jul 31, 2024 at 4:23 PM organicoman via Std-Proposals
<std-proposals_at_[hidden]> wrote:
>
> This is my proposal(C++17 based) hopefully somebody will rewrite it in a better standard way.
> ----------------------------------------------------
>
> Operator
> effdecltype(expression)
>
> Other plausible name : typeof (expr)
>
> Intro
> C++ Is a type strong programming language, each object used has a well known type.
> For all object instantiated/defined in a program there is a one to one mapping between the name and the type it belongs to.
> Except for 3 categories:
> 1- Arrays
> 2- Function templates
> 3- Variable templates
>
> Collapsing type signature into one type id, makes tracing back what was passed/called impossible. And it is an aberration to the strong typing adopted by C++ language.

OK, I've been barely following this thread, in part because of your
frequent use of color to mean... a variety of things (sometimes it's
used for quotes from other people, sometimes a color is used to
indicate a text box, etc), which makes it hard to follow. So it's good
to see a clear, identifiable proposal.

So we can start by saying... no, that's not how C++ works, nor is it
how C++ is *intended* to work.

*Expressions* have types; names are used to identify language
entities. A name alone *can* be an expression, which identifies a
particular entity, and the nature of that entity is used to determine
the type of that expression. But a name is not by itself a typed
thing.

The 1:1 mapping you imagine is not intended by the language.

> Let's illustrate that with some examples:
> Category 1:
>
> #include <type_traits>
> template<typename T>
> void foo(T t)
> {
> static_assert(std::is_same_v<T, char*>);
> }
> int main()
> {
> char arr[5];
> foo(arr); // #1 assertion passed
> static_assert(std::is_same_v<decltype(arr),
> char[5]>); // #2 assertion passed
> }
>
> In the example above, the same variable arr has two different types
> #1 char*
> #2 char[5]
> That's what i mean by type signature collapsing. The standard calls it array pointer decay.

But `arr` doesn't have two types. `foo`'s parameter `t` has a
different type from `arr`. The problem here is not with `arr`; it's
with what happens when `t` is deduced from `arr`. Indeed, if your
template is written to expect an array, it can deduce the array
correctly.

```
template<typename T, std::size_t N>
void foo(T (&t)[N]);
```

This will capture the full type information of `arr`.

> Category 2:
>
> #include<type_traits>
> template<typename T>
> void foo() { }
>
> int main()
> {
> auto foo_1 = &foo<int>;
> auto foo_2 = &foo<double>;
> static_assert
> (
> std::is_same_v
> <
> decltype(foo_1)
> , decltype(foo_2)
> >
> ); // #1 assertion passed
> }
>
> In this example, we have two functions instances with different type signature, yet their type id is the same.

But they don't have "different type signature"; they have the *same*
type signature. In both instances, they are the type `void(*)()`. The
signature of that function pointer is `void()`. Perhaps you're using
the term "signature" in a more vernacular sense. But you're writing a
proposal for the standard. You need to use standard terminology where
possible.

Furthermore, you are making the same mistake you made with the `arr`
issue: you expect that `foo_1` and `&foo<int>` are the same thing.
That `foo_1` is just shorthand for `&foo<int>`. They are not, and the
language does not intend for them to be the same.

> Category 3:
>
> #include<type_traits>
> template<typename T>
> int Var = 123;
>
> int main()
> {
> auto var_1 = Var<double>;
> auto var_2 = Var<char>;
> static_assert
> (
> std::is_same_v
> <
> decltype(var_1)
> , decltype(var_2)
> >
> ); // #1 assertion passed
> }
>
> The same as well for variable templates.
> Different signature collapsed to same type id.

There isn't even a function signature here, so again, I must assume
you're referring to a more vernacular concept. So... what *is* that
concept that you're talking about? What do you mean by "signature"?

Because the standard is very clear: the type of `Var<double>` is
`int`. It is not intended to be anything other than `int`. The
behavior of `auto var_1 = Var<double>;` and `int var_1 = Var<double>;`
is meant to be the same.

If you want to change this behavior, you need to justify why `auto`
can do something that cannot be done by directly using the type that
would be deduced by `auto`.

> The standard doesn't explain why it adopted this behavior, except for the case of array pointer decay.
> But from the examples above, we can observe a recurring pattern.

The standard describes how behavior works, not *why* it works that way.

> There are two different types, one written by the user, and one seen by the compiler.

This is probably the most confusing part. Because `Var<double>` is not
a type; it is a variable. That variable has a type, and that type is
clearly `int`.

You are trying to manufacture a meaning that does not exist and is not
meant to exist.

I'm snipping the rest of this off because it doesn't really matter.
What you're proposing doesn't make sense with the type system.

Received on 2024-07-31 22:27:49