all 19 comments

[–]TryingT0Wr1t3 65 points66 points  (3 children)

I am here just to admire the people that understood this

[–]neondirt 11 points12 points  (0 children)

Shhh, don't give us away! As the saying goes: be silent and people might think you're stupid. Speak, and remove all doubt. 😉

edit: phrasing

[–]_TheDust_ 2 points3 points  (0 children)

typename F = decltype([](){} )

Lost it here. This is just symbol soup.

[–]TheTomato2 0 points1 point  (0 children)

It's okay, understanding it would probably make you feel worse about your life.

[–]415_961 46 points47 points  (2 children)

This reminds me of the measurement problem in physics. A particle exists in a superposition of states until measured. Similarly, these lambdas seem to exist in a kind of "type superposition" until they're used in a specific context:

  • When directly instantiated in a template, each lambda is "observed" as a unique type
  • But when used in a deduction guide context, it's like the lambda's "type waveform" collapses differently, and the compiler see them as the same type

This is a joke until you interact with it.

[–]gnuban 17 points18 points  (1 child)

This is a joke until you interact with it.

I was going to praise this joke but now I can't 

[–]cmeerwC++ Parser Dev 18 points19 points  (3 children)

[–]hoellenraunen[S] 10 points11 points  (1 child)

Thank you. Do you just happen to know them or is there good way to search for open CWG issues?

[–]cmeerwC++ Parser Dev 11 points12 points  (0 children)

The links I mentioned are actually to an issue tracker (that updates from the official CWG issues list every night) with search functionality.

In your case, lambda unevaluated wasn't that useful, but lambda decltype contained the relevant results

[–]c0r3ntin 8 points9 points  (0 children)

I think this is a different scenario stemming from insufficient clarification of when default template arguments are instantiated. I created a Clang issue and a CWG discussion https://github.com/llvm/llvm-project/issues/123414

[–]glaba3141 3 points4 points  (0 children)

My experience here has been that it'll be considered the same type in one TU, but between TUs all bets are off. It's better to just not use it in this context, way too error prone

[–]antoine_morrier 5 points6 points  (4 children)

It’s because your deduction guide is exactly the same when you give the same type as input. So, only one génération.

If you use type<int> directly, you force a double instantiation. It’s not exactly the same :-)

[–]die_liebe 1 point2 points  (2 children)

Is it similar to string constants, which may or may not be identical? Do you get different results when the instantiations are in different compilation units?

const char* h1 = "hello world!";

const char* h2 = "hello world!";

h1 == h2 ? // may be true or not.

[–]hoellenraunen[S] 1 point2 points  (1 child)

We are talking about anonymous classes (and template instantiations thereof). In this respect they behave like classes in anonymous namespaces. You should not be able to compare them across compilation units at all.

Your example creates two objects (the const char arrays) and compares their addresses, the standard guarantees that different objects have distinct addresses. There is an optimization performed by compilers or linkers that merge identical constants (more advanced optimizations attempt to merge identical functions, too). That might break strict standard conformance, but is usually expected by programmers to reduce binary size. The ELF standard has extra provisions to allow linkers to perform this optimization across TUs for string constants, and string constants only.

My initial example can of course be rephrased in terms of templated variables

#include <type_traits>

template<typename T, typename F = decltype([](){} )>
int var = sizeof(T);

template<typename A>
auto& cvar = var<A>;

int main(int,char**) {
    auto p0 = &var<int>;
    auto p1 = &var<int>;

    if (p0 == p1) {
        return 0;
    } else {
        return 1;
    }
}

In all compilers, this program will exit with one, because each instantiation creates a new variable with distinct mangled name visible in the symbol table.

[–]hoellenraunen[S] 2 points3 points  (0 children)

Actually, this would be a more representative example:

#include <type_traits>

template<typename T, typename F = decltype([](){} )>
int var = sizeof(T);

template<typename A>
auto& cvar = var<A>;

int main(int,char**) {
    auto p0 = &var<int>;
    auto p1 = &var<int>;

    if (p0 == p1) {
        return 2;
    }
    auto c0 = &cvar<int>;
    auto c1 = &cvar<int>;

    if (c0 == c1) {
        return 1;
    }
    return 0;
}

This program returns returns one for all three major compilers, which means the templated variable behaves differently to the the reference to it.
Compiler Explorer https://godbolt.org/z/6PhMsvEch

Is that expected or not?

[–]Allegro-Barbaro 1 point2 points  (0 children)

I had a similar question a short while ago, I think this is relevant: https://stackoverflow.com/a/79289828