6
$\begingroup$

In C++, there are two newly created features related to const: constexpr and rvalue references. (IMHO rvalue reference is not designed well. But here it refers to any fixed design without adding another dimension of characteristics that needed to be recorded somewhere. Say it could be properly type checked, and a function could support both lvalue and rvalue references without difficulty.)

But I'm thinking in reverse: If constexpr and rvalue references already existed in a language, how necessary is it to add const?

Firstly, const could be used for access control. Languages that lacked other means of access control, such as protected, private, tend to also not care much about const. Now we assume it is the case. We don't have to protect anything from mutation. We don't care if someone creates the surprise by True = False. Then does const add other value to the language? For example, could references to a constant in parameter declarations be fully replaced by rvalue references?

On the other hand, does it add value even if we need access control? Could we possibly change all such uses to a possibly improved version of constexpr, and protected variables that doesn't reveal a direct write interface?

$\endgroup$
5
  • $\begingroup$ You should also take into account adding mutable. From my understanding, const with mutable is in practice completely different from constexpr. However, I'm not too deep into the C++ semantics. $\endgroup$
    – feldentm
    Commented Jan 26 at 12:36
  • 1
    $\begingroup$ const is often used with pointers, to indicate that the data it points to will not be modified. I don't think it's obviated by the existence of constexpr. $\endgroup$
    – Barmar
    Commented Jan 26 at 23:43
  • $\begingroup$ E.g. const char *foo; means the string won't be modified. $\endgroup$
    – Barmar
    Commented Jan 26 at 23:44
  • $\begingroup$ const is used with pointers to indicate that the data it points to will not be modified through that pointer. $\endgroup$ Commented Jan 27 at 13:07
  • 2
    $\begingroup$ Also, what do rvalue references have to do with anything? Rvalue references can mutate, in fact that’s their point. $\endgroup$ Commented Jan 27 at 13:10

2 Answers 2

3
$\begingroup$

const and constexpr are not interchangeable. They are both useful in situations where the other is not.

The value of a constexpr variable is always computed at compile time. It can thus be used in contexts like array sizes or template parameters, but it cannot hold values computed from e.g. user input.

A const variable may be initialized using information only available at runtime, and then stays unmodified afterwards. It can be used to inform the reader and the optimizer that an object will not be modified by other parts of the program. (Note, however, that this is only a guarantee, on penalty of undefined behavior, for objects and not for pointers.)

$\endgroup$
-1
$\begingroup$

A possibly overly complex self answer:

Compared to copying a value to avoid modification, while const may appear to be just an optimization, there are also important semantic differences. A constant reference could refer to the exact original object. Copying may ruin internal cross references.

With this in consideration, a constant reference have two uses. One is to provide a read-only interface, and do the writing in another interface. It is technically how const is defined, but its usefulness is dubious. Most likely const is not enough for this purpose, and one should consider const and other relevant features as a whole.

The other is to let it refer to an exact version (or assignment) of the variable, though usually not enforced in a language. If we relax it a bit, that it could be changed but must be reverted to the original state before deallocating the reference (such as exiting the function), with some additional restrictions such as ownership, the timeline of the modification of the variable would form a tree, instead of a line.

The later use means the value is constant over the lifetime of the reference. Compared to rvalue reference, which should be deallocated after the lifetime of the reference in a better design, where deallocation means it has reduced to one possibility like a constant; and constexpr, or its counterpart as an argument, template arguments, which could be not allocated yet before any use of it. There are more to be dug underneath, as constexpr is not the proper counterpart of rvalue reference. A better counterpart is the C# out parameters, which works like a return result. But all of them are obviously different from the normal constants.

Thus the common use of constant to accept both references to mutables and temporary objects doesn't sound reasonable, because no branches are formed in the timeline. An optionally rvalue reference should be better.

But then the problem is, why were const used as such? How else could it be interpreted if someone insists on using const for this purpose?

To analyze this problem, we could think every parameter split as an input and an output. The problem for mutable lvalue parameters substituted as a temporary value is, the "meaning" of the input is supposedly consumed by the function, and the output is supposedly recreated with a new meaning. If the parameter is substituted as a temporary value, the newly created meaning is thrown away, which could be possibly an error. And unlike uninitialized variables, some languages decided it should be a fatal instead of a warning.

Const and rvalue reference are two different approaches to say it is not the case. Rvalue reference means the output is meaningless. It could contain garbage. And there could also be no output in a different language design. Const means the output is not linked to anything. The substituted value is linked to the input, and it stays as the input after the function call ends. Const could cover what should be done using rvalue references, because there isn't an output side of a parameter, so one doesn't need to specify whether the output and the linked object is "closed" (or destructed).

Things would be much clearer if we use the reverting interpretation. We could think a const parameter has an output, that is a reference that accepts writing, but would be reverted after the reference is deallocated. So whether it is considered lvalue or rvalue could be about what would be done to that reference. Rvalue means an immediate deallocation. Though that reference is hidden or at best considered consumed by the function itself in most languages, and it won't generate errors because we could assume the function is intended for exactly what it does, that is to consume or throw away the new meaning.

By this logic, const rvalue marks the input unchanged and the output hidden, supposedly with garbage if exposed; mutable rvalue returns garbage; const lvalue marks the input unchanged and export another output as a revertible reference; mutable lvalue follows the modified state. By optionally rvalue, it could make an output either garbage or useful data. But by optionally const, it may still generate another output, that is a revertible reference, and throw that away. So it might be not as satisfactory. Practical usages might be both optionally rvalue and optionally const for other reasons, though.

The implication is, a mutable turned into const should be considered a separate entity, that could be captured into an rvalue reference, which only marks the lifetime of the const wrapper, without destroying the original mutable variable. I wanted to say "unlike C++" but found that rvalue references to const isn't frequently used in C++, and the current few usages couldn't decisively demonstrate it should not be used for what I devise. A const lvalue substituted as a parameter accepts hypothetical revertible edits that don't exist in C++ compared to a const rvalue. Another implication is there are multiple "outputs" on a parameter, but the details might be too far from the original question.

(I'm not using the precise terms like glvalue because the exact differences probably don't apply to a redesigned language. "Optionally const" includes statically optionally const which could change return type and other types.)

$\endgroup$
1
  • $\begingroup$ I'm afraid this is completely incomprehensible. Seems like maybe you are considering only function parameter types? But even then, I don't see how to reconcile how those work in C++ and whatever it is you're trying to describe here. $\endgroup$
    – Mat
    Commented Jan 31 at 20:08

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .