1

This is sort of a follow up to this question

Consider the following snippet

void f(int const &); // f "guarantees" not to change its parameter

int main()
{
  int const a = 42;  // a is not modifiable
  f(a); // so f definitely can't modify a 
        // (at least not without invoking UB), that's great

  int b = 42; // b is modifiable
  f(b); // but f could modify b legally? huh? not so great
}

My understanding: f can only modify b, and it must use const_cast to do so. Also, this is generally a bad idea, and should not be done. Also, there are reasons for why there is a mechanism in the language to ignore const.

If my understanding is correct, then my question is, is there a way to write a function that is guaranteed to not modify its argument. i.e. is there a way to do the following

void f(int const_really &); // f *guarantees* not to change its parameter

int main()
{
  int b = 42; // even if b is modifiable
  f(b); // f is not allowed to modify b legally
        // (or is UB if it does)
}

Edit: One reason for doing this would be to allow the compiler to do optimizations. Currently, a function that accepts an argument by const-reference is not allowed to assume that the bits of the argument will not be modified by the function. This is because the function could do a legal const_cast, or the argument type could have mutable members.

If the function could rely on the argument not being modified, then I would assume that certain kinds of optimizations could be done (similar to whole-program optimizations that prove the argument never changes).

Is that something that could be added to the language?

If not, is there some reason why this can never be done?

cigien
  • 57,834
  • 11
  • 73
  • 112
  • `(at least not without invoking UB)` seems like it will always apply to C++ code. C++ has unsafe escape hatches as a language feature, to let you do things like treat any type as just bytes. – Dave S Feb 28 '20 at 22:38
  • 3
    The obvious counter question would be *why* you need to do this. C++ is designed around trusting the programmer to not do bad things, or them knowing exactly why they are doing bad things. Such "safety guarantees" don't really fit the design of the language – UnholySheep Feb 28 '20 at 22:38
  • 3
    If you care that much about `f` not modifying `b`, then pass in a copy of `b`. – Brian Bi Feb 28 '20 at 22:51
  • I could maybe see a case for allowing compiler optimizations to know the value of `b` has not changed after `f` returns, but it's not clear how helpful that would be. – aschepler Feb 28 '20 at 22:59
  • @Brian ok if it's an int, but what if it's an expensive type to copy? – cigien Feb 28 '20 at 23:09
  • @UnholySheep I used to think const made such a guarantee. In fact, I thought c++ let programmers do dangerous things, _when it makes sense to do so_. Isn't it reasonable to have guarantees that things that never make sense, are not allowed? – cigien Feb 28 '20 at 23:11
  • @cigien how would you implement the mechanism behind this guarantee on, say, x86 hardware? – DeducibleSteak Feb 28 '20 at 23:13
  • @DeducibleSteak Not the faintest idea. However, I think this is more of a language question, i.e. I don't expect the compiler to diagnose this in any way, I'd just like the language to say it's not allowed. – cigien Feb 28 '20 at 23:23
  • "Doctor, it hurts when I do this." So, don't do that. – Spencer Feb 28 '20 at 23:24
  • You're already using `const`. If your code bypasses that (e.g. by casting way the `const`, accessing the passed argument by some other means (e.g. via a pointer to the object that is passed by some other means)) then all bets are off. Casting away `const` and then modifying the object often gives undefined behaviour, and there is no way to prevent that other than directing the programmer "don't do that". If programmers are determined enough to change the object, you can't prevent that. – Peter Feb 28 '20 at 23:33

2 Answers2

4

The keyword for that is const. It already offers as strong a guarantee as C++ will ever give - that is none, if the implementer of the function chooses so.

If you decide to cast away const, all bets are off, and you better know what you are doing.

C++ is a language that will always allow you to cast away anything, in one way or another. So whatever new const_really keyword you introduce, it will be left right where const is now - powerless if the programmer decides to ignore it and do a bunch of casts. Disallowing that will go against the core tenet of C++ - absolute control, if the programmer needs it. There is no need for a new keyword, because there is nothing to be gained from adding one.

DeducibleSteak
  • 1,398
  • 11
  • 23
  • I disagree with your last paragraph. There is nothing stopping the standard committee from introducing a new type of const that can't be cast away. – NathanOliver Feb 28 '20 at 22:56
  • 1
    @NathanOliver I think the second-to-last sentence sufficiently explains why the standard committee will likely never do that. – Patrick Roberts Feb 28 '20 at 22:59
  • 1
    Sure, the commitee might introduce something like that - but what lengths will they have to go to, to guarantee that I won't find a creative way around it? I can think of a miriad of ways to change the value of a variable in memory; C++ is a language that basically allows you to drop into assembler at any point, after all. I just don't think that's in the cards for what there is to be gained - I'm willing to be persuaded otherwise, if there is actually a good use case for this, but I just don't see it. – DeducibleSteak Feb 28 '20 at 23:06
  • But c++ doesn't let you cast away anything, e.g. in the example, casting away const on a is UB, even though the programmer _could_ do it. – cigien Feb 28 '20 at 23:07
  • 1
    But C++ _does_ let you cast it away. It's just that _you_ will have to deal with the consequences, when that UB occurs. But, OK, I maybe I am really missing something here - can you present a use case for where this might be useful? Is there some particular effect in a real program that you are trying to achieve? – DeducibleSteak Feb 28 '20 at 23:10
  • 2
    It could be a useful thing to guarantee const, but that would be an evolution of the language as the current language does not allow this, and would thus require and ABI break. However, the standards committee recently voted to NOT break ABI, committing us to another few years of painful backwards compatibility. See their notes on the discussion here: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1863r1.pdf – GGibson Feb 28 '20 at 23:12
  • @DeducibleSteak I used to think const made this guarantee, so now I know it doesn't, I guess _every_ place that I use const, I now have to check whether I'm passing in a const object or not. The effect I'm trying to achieve is the const_really behaviour. – cigien Feb 28 '20 at 23:14
  • @GGibson Oh, that's interesting. So I take it it's possible in theory, but not something we'll see in the language for a while (or ever)? – cigien Feb 28 '20 at 23:17
  • 1
    Yes, but what would you gain from having such a keyword? If you don't trust the implementer of a particular function, the only option you have is not calling it. There are a lot of other ways a bad function can wreak havoc on your program, by design or by accident, other than casting away const. What is the real net benefit you would expect to get? – DeducibleSteak Feb 28 '20 at 23:17
  • @DeducibleSteak That's a fair point. There are lots of ways for a function to do bad things. I just discovered today that this is one of the bad thing that a function could legally do, and so I'm upset ;( Oh well, maybe this will get fixed someday. – cigien Feb 28 '20 at 23:21
  • @DeducibleSteak I suppose the benefit would be 1) You can safely provide fast access to a direct memory location 2) and know that the value won't change. I don't particularly care about such an optimization, but I'm sure some people would. – GGibson Feb 28 '20 at 23:22
  • Just to clarify, are trying to make a coding accident less likely, e.g., corruption of the value by accidentally casting away the constness? Or are you trying to protect against a malicious attack - either by the implementer of the function, or, say, a hacker that has found a vulnerability in the function? – DeducibleSteak Feb 28 '20 at 23:25
  • @cigien Right, it's ABI restrictions like this that inspired newer languages like Rust and Nim that wanted to design a language without the shackles of historical contingency. – GGibson Feb 28 '20 at 23:26
  • @GGibson, but doesn't Rust allow you to type "unsafe" and then do whatever, including calling C functions? How different is that from casting away constness? (I don't mean to denigrate the safety Rust offers, I love it, it's just that I don't see how it helps in this context.) – DeducibleSteak Feb 28 '20 at 23:29
  • @DeducibleSteak I'm not worried about malicious attacks really. Just accidental mistakes, but I suppose enough people know not to use const_cast without thinking about it hard. So this might not be much of a problem. – cigien Feb 28 '20 at 23:30
  • @DeducibleSteak Sorry, I have no idea what Rust allows or doesn't allow, except that it is different from C++ in part because the things they wanted Rust to have would have required breaking ABI in C++ if they tried to add those features to C++. – GGibson Feb 28 '20 at 23:34
  • 2
    @cigien, if that's any consolation, I've never seen this to be a problem in ~20 years of programming - if someone "accidentally" casts away const and does something he shouldn't, there's usually many more reasons not to use his code before you discover the const casting :) I don't think this is worth worrying over. – DeducibleSteak Feb 28 '20 at 23:38
  • 1
    `const` in the source code doesn't protect a program from malicious attack. `const` is a programming tool so you have info/clue that a data shouldn't be modified as designed by the writer. – acegs Feb 28 '20 at 23:40
0

const is the best you can have.

If you are worried that some external program can still magically modify the parameter b, that can possibly happen, even if you use other language like C#, Java, etc. Once you have a grasp of the address of b accidentally/intentionally and you specify an access like in kernel-mode or const_cast you can modify it. Anything can be modified internally.

You just now have to focus inside your function. Adding const is enough. If YOU didn't modify b inside YOUR function that's enough proof that b won't be modified even if can still be modified in a hack way. You know the full logic inside of the function. You can see that b won't be modified.

In addition, const_cast have a useful purpose. In my case, I have getters of big object that I have to return as const so most of member variables and methods won't be modified or accessed. But I have to set a special methods to add things on that big object. It's just like categorize access. I hope they'll add it to c++ someday.

acegs
  • 2,621
  • 1
  • 22
  • 31