On Mon, Apr 28, 2025 at 12:56 PM Arthur O'Dwyer <arthur.j.odwyer@gmail.com> wrote:
On Sun, Apr 27, 2025 at 1:22 PM Brian Bi via Std-Proposals <std-proposals@lists.isocpp.org> wrote:
On Sun, Apr 27, 2025 at 12:42 PM Tiago Freire via Std-Proposals <std-proposals@lists.isocpp.org> wrote:
> Why do you want different things to pretend they're the same thing? If you want the language to be changed, you need to offer a reason why it would be good, not simply "why not".

My proposition was that because it could produce smaller binaries (and allow for better code optimization) is itself reason enough to make the change.
And I'm trying to challenge this idea that "functions have different names and therefore should have different addresses, just because".
Same logic applies as to why zero size objects shouldn't have at least 1byte. no_unique_address should be the default and not the opt-in exception IMO.
[...]
A function is not an object, it is a function.
There's no reason to reason about functions as if they were objects.
 
we use the fact that different functions have different addresses to produce 128 unique values of type `TemplateTestFacility::MethodPtr` (which is a typedef to a pointer to member function), which are then used to test e.g. containers that must work with various kinds of element types.

That's not quite right [although see below]. You don't rely on the fact that different functions have different addresses; you rely on the fact that functions with different behavior must necessarily have different addresses.
You use (here):
    template <int IDENTIFIER>
    int TemplateTestFacility_StubClass::method() {
      return IDENTIFIER;
    }
    static_assert(&method<1> != &method<2>);

Ah, right. But I think the only reason we have to give them different behaviour is that MSVC does the standard-noncompliant merging by default. There are probably codebases somewhere that support only Unix, and use empty bodies, and we shouldn't break those users.
 

I assume Tiago doesn't propose to break that. He would simply standardize the somewhat-existing practice that we could reasonably put identical functions at identical addresses. (E.g. I know Green Hills' linker has done this for >20 years. But I don't know how Green Hills solves the problem below: whether it turns off the optimization if the address is ever taken, or just lets the behavior be unpredictable at runtime, or what.)

    template <int IDENTIFIER>
    int TemplateTestFacility_StubClass::method() {
      return 0;
    }
    assert_possibly_true(&method<1> == &method<2>);

Although notice that this is tricky for two reasons. One, the optimizer may cause functions that "look" different to actually have the same behavior after optimization. A classic example is:

    template<class T> int distance(T *a, T *b) { return b - a; }
    static_assert(&distance<int> != &distance<short>);  // must have different codegen, since sizeof(int) != sizeof(short)
    assert_possibly_true(&distance<int> == &distance<float>);  // can have identical codegen, since sizeof(int) == sizeof(float)

Two, letting the value of (fptr1 == fptr2) be unspecified at compile time means that expressions formerly compile-time-constant are no longer compile-time-constant. For example, today this is legal C++:
    int f();
    int g();
    constexpr bool b = (f == g);
    static_assert(b == false);
Making it legal for (f == g) at runtime [that is, "after link-time"] would make the compiler unable to compile this program. We'd have to bite the bullet and add programs like this to Annex C — "used to be valid C++, but no longer." This is totally achievable — after all, this is the very reason why we have an Annex C — but it certainly puts an item in the "Cons" column of our "Pros and Cons" table.

In fact, I guess this brings me full circle back to Brian Bi's point. Consider:
    int f();
    int g();
    template<auto*> struct A {};
    A<&f> a;
    A<&g> b;
    static_assert(!__is_same(decltype(a), decltype(b)));

If `&f == &g`, then this breaks down: Surely `A<value>` must be the same type whenever `value` is the same value! And if `&f` and `&g` have the same value, then `A<&f>` and `A<&g>` must be the same type. But we won't know whether they're the same type until link-time. So... we can't know at compile-time whether these two types are the same or different? That's... not something we can physically represent in C++.
So, to make this idea work at all, we would have to retroactively make it invalid to use the address of a function as a template argument — in the same way it's currently invalid to use the address of a string literal.
But that would almost certainly break someone's code. (Not Brian's example, AFAICT, since he never uses the unique function address as a template argument. But surely it'll break someone's code.)

HTH,
Arthur


--
Brian Bi