C++ Logo

std-discussion

Advanced search

Re: Unclear template type equivalence for decltype(dependent expression)

From: Jens Maurer <jens.maurer_at_[hidden]>
Date: Mon, 11 Nov 2024 22:53:43 +0100
On 11/11/2024 22.12, mauro russo via Std-Discussion wrote:
> Looking at the current draft exposed by https://eel.is/c++draft/#temp <https://eel.is/c++draft/#temp> as generate on 9th of Nov 2024, (as well as since at least the last draft n4849 for C++20),
>
> §13.6 ([temp.type]) - 5 reads
>
> "If an expression e is type-dependent <https://eel.is/c++draft/temp.dep.expr>, decltype(e) denotes a unique dependent type. <https://eel.is/c++draft/temp.type#4.sentence-1>
>
> Two such /decltype-specifier/ <https://eel.is/c++draft/dcl.type.decltype#nt:decltype-specifier>/s/ refer to the same type only if their /expression/ <https://eel.is/c++draft/expr.comma#nt:expression>/s/ are equivalent ([temp.over.link] <https://eel.is/c++draft/temp.over.link>). <https://eel.is/c++draft/temp.type#4.sentence-2>"
>
> I am not sure whether this wording applies to any context, not just to template arguments in template-ids (which is the topic of [temp.type].

[temp.type] is talking about equivalence of dependent types in general.
(Note this says "equivalence" not "the same". The latter is applicable
when nothing is dependent.)
It just so happens that the equivalence of template-ids is
an important factor for that.

> Moreover, the comprehension should rely on the clause §13.7.6.1 ([temp.over.link]) where the point 5 reads
>
> "Two expressions involving template parameters are considered /equivalent/ <https://eel.is/c++draft/temp.over.link#def:equivalent,expressions> if two function definitions containing the expressions would satisfy the one-definition rule <https://eel.is/c++draft/basic.def.odr>, except that the tokens used to name the template parameters may differ as long as a token used to name a template parameter in one expression is replaced by another token that names the same template parameter in the other expression. <https://eel.is/c++draft/temp.over.link#5.sentence-1> ..."
>
> Here doubts start more and more.
>
> "if two function definitions containing the expressions..." -> should it mean in the declarative part only of the definition or even in the body ?

In the body. We want to apply the one-definition rule on the result.

> In the former case, does it mean the two expressions have the same role (e.g., return type, type of the same-index parameter, etc.) ? No other requirements of 'similarity' for these two functions ? I really believe more information should be provided about how to construct these two function definitions, likely starting to indicate that the intent is two definitions of the same function.

It says we want to construct something to which the ODR can be applied.
The ODR can't be applied to non-defining declarations, so the other
reading doesn't make sense.

> And then, how a function (definition) may be in a scope involving template parameters except being a template(d) function member of a template(d) class... that is why not clarifying 'templated' for these two functions ?
> The need of clarifying 'templated functions' seems to derive even by the fact that we are discussing about some 'equivalence', and two different definitions for the same non-inline non-templated function violates the ODR rule (regardless of the sequence of tokens: refer §6.3 [basic.def.odr] - (15.1)).

Right, but we want to use the ODR mechanism to compare those
two hypothetical function definitions. There's no need
to talk about "templated", I think; we're just using
the "same tokens -- same meaning" list from the ODR.

> Additionally, looking at §6.3-(16.3):
> "If D is a template and is defined in more than one translation unit, the requirements apply both to names from the template's enclosing scope used in the template definition, and also to dependent names at the point of instantiation ([temp.dep] <https://eel.is/c++draft/temp.dep>). <https://eel.is/c++draft/basic.def.odr#16.3.sentence-1>"
>
> I guess that problems may arise by using 'templated functions' (in an eventual fix of §13.7.6.1-5) instead of 'template functions', because §6.3-(16.3) refers applying requirements to "names from the enclosing scope" only for templates, not for templated entities. Of course, the intent of 6.3-(16.3) also implicitly covers the templated entities included in a template... but for the two hypothetical definitions referred by §13.7.6.1-5, they appear as 'the starting point', that is, not caring of any hypothetical enclosing template to which §6.3-(16.3) would be applicable.

Put differently, both hypothetical definitions would be in the same context,
so there is no change in interpreting names referring to the "outside"
between the two hypothetical definitions.

> Coming back to §13.6-5, do you really believe that the two templates in the example of stack overflow <https://stackoverflow.com/questions/79040452/unique-dependent-type-for-decltypedependent-expression-in-c>:
>
> |X<decltype(t+t)> a; X<T> b;|
>
> should be the same type, and that §13.6-5 correctly supports this equivalence ?

> I don't see so, based on the fact that §13.6-5 refers §13.7.6.1-5 that, in turn, refers §6.3 which finally requires the same sequence of tokens. Well, §13.7.6.1-5 reads that different parameter naming is anyway acceptable as relaxation compared to the requirements from §6.3, but the provided example does not match such an exception. Really, in my example we have one decltype(e) and one non-decltype, for which §13.6 might say something more, ... but even using X<decltype(t+t+t)> for 'b' object, I still get the same type from several compilers, a comprehensible intent for the language, likely supported by the aforementioned standard clauses in a non-very clear manner.

[temp.type] is for declaration matching and similar situations
where types need to be compared before instantiation.

In your example, the function template "f" is instantiated when
that's needed (because you call it from "main"), and the instantiation
produces a non-dependent function body by substituting "int" for "T"
everywhere. Since nothing is dependent anymore after this, the special
rule for decltype(e) in [temp.type] doesn't apply at this stage.

Jens

Received on 2024-11-11 21:53:48