Date: Sun, 8 Jan 2023 18:50:31 -0500
On Sun, Jan 8, 2023 at 4:27 PM Julien Villemure-Fréchette via
Std-Proposals <std-proposals_at_[hidden]> wrote:
>
> Right now, the specification of `decltype(expr)` have two mutually distinct behaviors:
> 1. id expr or member access expr: refers to the type of the declaration that introduced the id expr.
> 2. otherwise: refers to the type and value category of expr, if expr were to be evaluated.
>
> Both are distinct enough operations, but they still use exactly the same syntax, and interpretation rely on semantic properties of `expr`. AFAIK, there doesn't seem to be any generic context where we could want a single `decltype(expr)` be interpreted in one way or the other depending on the value or type of a template parameter. Either we force behavior 2 with `decltype((expr))`, otherwise we 'hope' that other developers get that we intended behavior 1 if `expr` appears to be, say a member access expression `v.m1`. But, suppose at some point we refactor code, make the member private and define an accessor function instead, then the interpretation of `decltype(v.get_m1())` will most likely have changed of meaning, without any compiler diagnostic.
The problem is deeper than this: `v.m1` and `v.get_m1()` are not and
have never been equivalent expressions. They can be used
interchangeably in many ways, but they're not the same. So you cannot
just blindly change them without causing potentially other problems.
> Another oddity about decltype is that behavior 2 is achieved by use of "syntactic cleverness". There's nothing special about the extra pair of parentheses: if we want to force behavior 2 we *may* use `decltype((expr))`, but we may as well write `decltype(0, expr)`, or `decltype(*&expr)` (if expr is an id expression or member access). Considering that behavior 2 accounts for most uses of `decltype`, I think it deserves to be expressed in a form which is 'non compound' and free of syntactic cleverness.
Consider this:
[](auto ...&&args) { return func(std::forward<decltype(args)>(args)...);}
That `decltype(args)` uses behavior 1. And it is *really important*
that it uses behavior 1. So I contest the idea that most uses of
`decltype(id_expression)` intend to use behavior 2.
> In ideal, at the time decltype keyword got introduced, I think we should have instead introduced two keywords (say `exprtype` and `decltype`), each in one-one relationship with their respective behaviors:
The current situation is kind of crufty, but ultimately this is not
worth two keywords. It *certainly* isn't worth *three*.
Std-Proposals <std-proposals_at_[hidden]> wrote:
>
> Right now, the specification of `decltype(expr)` have two mutually distinct behaviors:
> 1. id expr or member access expr: refers to the type of the declaration that introduced the id expr.
> 2. otherwise: refers to the type and value category of expr, if expr were to be evaluated.
>
> Both are distinct enough operations, but they still use exactly the same syntax, and interpretation rely on semantic properties of `expr`. AFAIK, there doesn't seem to be any generic context where we could want a single `decltype(expr)` be interpreted in one way or the other depending on the value or type of a template parameter. Either we force behavior 2 with `decltype((expr))`, otherwise we 'hope' that other developers get that we intended behavior 1 if `expr` appears to be, say a member access expression `v.m1`. But, suppose at some point we refactor code, make the member private and define an accessor function instead, then the interpretation of `decltype(v.get_m1())` will most likely have changed of meaning, without any compiler diagnostic.
The problem is deeper than this: `v.m1` and `v.get_m1()` are not and
have never been equivalent expressions. They can be used
interchangeably in many ways, but they're not the same. So you cannot
just blindly change them without causing potentially other problems.
> Another oddity about decltype is that behavior 2 is achieved by use of "syntactic cleverness". There's nothing special about the extra pair of parentheses: if we want to force behavior 2 we *may* use `decltype((expr))`, but we may as well write `decltype(0, expr)`, or `decltype(*&expr)` (if expr is an id expression or member access). Considering that behavior 2 accounts for most uses of `decltype`, I think it deserves to be expressed in a form which is 'non compound' and free of syntactic cleverness.
Consider this:
[](auto ...&&args) { return func(std::forward<decltype(args)>(args)...);}
That `decltype(args)` uses behavior 1. And it is *really important*
that it uses behavior 1. So I contest the idea that most uses of
`decltype(id_expression)` intend to use behavior 2.
> In ideal, at the time decltype keyword got introduced, I think we should have instead introduced two keywords (say `exprtype` and `decltype`), each in one-one relationship with their respective behaviors:
The current situation is kind of crufty, but ultimately this is not
worth two keywords. It *certainly* isn't worth *three*.
Received on 2023-01-08 23:50:38