3

Consider the following class template:

template <auto>
struct T {};
  • Are two specializations of T, where the respective template-argument is a pointer-to-member(1) to two different but same-type members, guaranteed to refer to different specializations of T?

(1) pointer to data member or pointer to member function.


Or, applied to the following example:

struct S {
    int x;
    int y;
    void f();
    void g();
};

static_assert(std::is_same_v<decltype(&S::x), decltype(&S::y)>);
static_assert(std::is_same_v<decltype(&S::f), decltype(&S::g)>);
  • Are the specializations T<&S::x> and T<&S::y> of T guaranteed to refer to different specializations of T?
  • Are the specializations T<&S::f> and T<&S::g> of T guaranteed to refer to different specializations of T?

or, in code, is the following snippet well-formed?

// T and S as above.
static_assert(!std::is_same_v<T<&S::x>, T<&S::y>>);
static_assert(!std::is_same_v<T<&S::f>, T<&S::g>>);
dfrib
  • 70,367
  • 12
  • 127
  • 192
  • As has been the case also for some previous occasions, my honest question ended up being answered by myself, during the process of writing it out in a thorough manner. – dfrib Nov 28 '20 at 10:58
  • 1
    I'm curious what is your motivation. `template struct T { auto foo(S s) { return s.*p; } };` must return the correct thing, how can `T` possibly do both simultaneously? – Passer By Nov 28 '20 at 11:29
  • @PasserBy motivation for the question, as in a use case for this, or why this is not directly self-apperent? The question is about normative references for the type identity of specializations of a class template for a particular use kind of non-type parameters, but I agree that your example shows that this must indeed be the case. – dfrib Nov 28 '20 at 12:25
  • They must be -- when you use the template variable, it has to do the right thing. How could it do the right thing if the right could be two different things? Remember, it's actually generating code and that generated code will use the offset of the member in the class. Different members have different offsets. If you somehow didn't use the value, then maybe the optimizer collapses them, but at the C++ level, they must be fundamentally different. – xaxxon Nov 29 '20 at 03:01

2 Answers2

4

All standard references below refer to N4659: March 2017 post-Kona working draft/C++17 DIS.


Yes, they are guaranteed to refer to different specializations.

As per [temp.names]/1

[temp.names]/1 A template specialization can be referred to by a template-id: [...]

a template specialization is formally referred to by a template-id, and as per [temp.type]/1, which governs type equivalence, particularly [temp.type]/1.5 [emphasis mine]

[temp.type]/1 Two template-ids refer to the same class, function, or variable if

  • /1.1 their template-names, operator-function-ids, or literal-operator-ids refer to the same template and
  • [...]
  • /1.5 their corresponding non-type template-arguments of pointer-to-member type refer to the same class member or are both the null member pointer value and [...]

does not apply for a template-id if their non-type template-arguments of pointer-to-member type refer to different class members of the same class (template).

For the example of

static_assert(!std::is_same_v<T<&S::x>, T<&S::y>>);
static_assert(!std::is_same_v<T<&S::f>, T<&S::g>>);

particularly, [temp.type]/1.1 applies and is fulfilled, whereas /1.5 applies and is not fulfilled (the remaining paragraphs of [temp.type]/1 does not apply here).

dfrib
  • 70,367
  • 12
  • 127
  • 192
  • Minor point, why are you listing all of [temp.type]/1 in the answer, instead of just 1.5? Seems like it would be clearer to read. – cigien Nov 29 '20 at 03:59
  • 1
    @cigien I mostly wanted to highlight that /1.1 also applies, albeit not they key here. Removed the noise of everything but /1.1 and /1.5. Thanks for the feedback. – dfrib Nov 29 '20 at 06:10
0

In theory, yes. In reality, consider

union A {
    int foo;
    int bar;
};
template<auto>
struct T{};

MSVC treats T<&A::foo> and T<&A::bar> as the same type.

cpplearner
  • 13,776
  • 2
  • 47
  • 72
  • 1
    As this is a language-lawyer question, "in theory" is what is of the main interest here. Whilst an interesting example, this arguably seems like an MSVC bug; were I to guess MSVC applies [\[expr.eq\]/3.5](https://timsong-cpp.github.io/cppwp/n4659/expr.eq#3.5), although it covers comparing pointers to members (for unions, specifically) and not type identity for templated entities with pointers to members supplied as template arguments. Unions are notorious for implementation variance, but you may want to attempt filing an MSVC bug whilst citing [temp.type]/1. – dfrib Nov 29 '20 at 06:05
  • ... [\[class.union\]/2](https://timsong-cpp.github.io/cppwp/n4659/class.union#2) may also add to MSVC:s confusion, as it explicitly mentions that non-static data member of unions are pointer-interchangeable. Afaict, [temp.type]/1 should apply likewise for members of union classes as for non-union classes. – dfrib Nov 29 '20 at 06:24
  • @dfrib confustion between non-static data members and member subobjects may add to everyone's confustion. – Language Lawyer Nov 29 '20 at 10:02
  • @LanguageLawyer Is any of the two standard references in my commenta above relating to member subobjects rather than non-static data members? Or do you mean tat this is a general confusion? :) – dfrib Nov 29 '20 at 10:20
  • @dfrib I mean there a general confusion between variables/objects and NSDMs/subobjects all over the standard :/ – Language Lawyer Nov 29 '20 at 11:36
  • @LanguageLawyer Ah yes, the _entities_ related confusion. – dfrib Nov 29 '20 at 12:12