C++ Logo

std-discussion

Advanced search

Why abandon lambda-styled captures in the latest Contracts MVP, and adopt the implicit const-ness instead?

From: vspefs <vspefs_at_[hidden]>
Date: Tue, 09 Apr 2024 13:31:08 +0000
To whom it may concern,

While reading P2900R5, I can't help but notice the implicit const-ness for identifiers and the tacit consent that they are regarded as const lvalues within condition expressions.

This, of course, is a way to imply that contracts are to observe, to check, not to modify. However, I do think I see some problems are there, which may lead to human confusion on multiple levels.

- People get confused by the "default" const-ness.

1. 'pre' and 'post' are keywords for contracts, which may indeed remind us of the original idea behind the implicit const-ness. However, it's common that once people begin to fill the parentheses, people forget about everything outside. And exactly, that's why some people tend to use manual prefix/postfix on each identifier to point out cv-qualifications, reference types and other things, which tradition now becomes inconsistent.

If const-ness is to be added to the identifiers in condition expressions, I think a more explicit way can be adopted, even if it means not doing so (attaching const-ness to identifiers) by default. Maybe like the const-specifier to member functions.

2. By const_cast<> and our powerful pointers, we can still modify the local variables and parameters. Besides, it's also open to modify namespace-scope or local static variables. But actually, in my opinion, that makes the whole 'safety' concern not safe at all.

First, if the whole contracts facility is to observe, why would we want "such accesses are more likely to be intentionally modifying" (quoted from the original paper) to be modified here, not anywhere else?

Second, it's so common that strict limitations, if not strictly applied, become no limitation at all. Because powerful features would easily be used as 'silver bullets' by someone who just wants a quick workaround and couldn't care less about original intentions behind features. If that idea lingers too long, it could easily become a 'legacy mis-usage'.

3. Some conceptual mazes here. As we all know, modifying a const value is considered to be an undefined behaviour. But what about here, in condition expressions, where identifiers are regarded as const lvalues? So modifying value here with some techniques, can be seen as an undefined behaviour 'locally', while it's legal 'globally'? Of course, it purely depends on how carefully one would read and interpret the standard, but less conceptual maze is better than more.

4. All identifiers are considered lvalues here. Not sure if that would lead to some unnecessary inconvenience, or worse, limitations.

- It's just that "implicit"s shouldn't be implicit.

1. This creates burden and stress for programmers when memorizing and applying Contracts. The evolution of C++ in recent years is impressive, yet not too energy-consuming to catch up. I don't see why we shouldn't keep this pace and create extra burden for learners.

2. We shouldn't be creating intuition-countering syntax and semantics. Intuition is important in programming as it's in various fields. For example, most of the time, intuition works when it comes to thinking about value categories, like whether we should use std::move() here, or not.

So, based on all mentioned above, I think the current MVP doesn't quite serve our purpose well. Instead, I'd propose the original syntax in P2461R1, which uses lambda-styled captures to specify the identifiers to be used in condition expressions. Or, I'd rather we totally ban modifications within condition expressions. However, I do agree that suitable changes can be make to improve the clarity and readability of our code.

If my idea appears to be inadequate, lack of basic knowledge, or already answered before, please do point me out.

Written on Tue, Apr 9, 2024 at 9:27 pm (UTC+8).

Received on 2024-04-09 13:31:24