pseudo constness

473 views
Skip to first unread message

jsaintm...@gmail.com

unread,
Feb 21, 2018, 6:00:10 PM2/21/18
to ISO C++ Standard - Future Proposals
One of my post on stackoverflow.com was said to have its place here.

https://stackoverflow.com/questions/48915277/what-about-pseudoconst-qualifier

What do you think about my new pseudoconst qualifier ?
Expecting for your answer, (and/or possibly a job: long unemployed at the moment)

Jerome Saint-Martin,
Montrouge, France



Jake Arkinstall

unread,
Feb 21, 2018, 6:23:12 PM2/21/18
to std-pr...@isocpp.org
Welcome!

Perhaps you can explain the consequences further, namely give an example of something useful that it would allow.

One of the benefits of having something const is not just that the data is unchanged at the END, but that it doesn't change AT ALL during execution. This is often useful in a threaded situation (when you want to make sure you don't introduce race conditions), and its primary purpose is to stop you from making mistakes rather than to optimise.

On top of this, the compiler is able to easily determine whether or not you are obeying the const declaration in the definition of a method, in that only const operations are used inside of it - how is the compiler supposed to verify if the second method actually reverses the first? Trust the developer? Because that's the situation we have without const already.

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/f202be10-efb5-4f1a-ab6a-18bf6ac873ee%40isocpp.org.

Richard Hodges

unread,
Feb 21, 2018, 6:24:28 PM2/21/18
to std-pr...@isocpp.org
On Thu, 22 Feb 2018 at 00:00, <jsaintm...@gmail.com> wrote:


When I examined your example on stack overflow, I wondered whether the. Or react solution would be to make the vector mutable, which is in a way, making the class “pseudo const”, no?

What do you think about my new pseudoconst qualifier ?
Expecting for your answer, (and/or possibly a job: long unemployed at the moment)

Jerome Saint-Martin,
Montrouge, France



--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.

Jake Arkinstall

unread,
Feb 21, 2018, 6:35:26 PM2/21/18
to std-pr...@isocpp.org
Also note that you're including the allocation of 1000 size_ts in that first timer. I'm not at my PC right now to test, but does moving the initialization of V to before the clock start significantly reduce the discrepancy?

Jake Arkinstall

unread,
Feb 21, 2018, 8:13:50 PM2/21/18
to std-pr...@isocpp.org
Further to my previous reply, I have written a rough version which should be a bit more reliable (it isn't perfect, but should suffice for the purpose of demonstration).

Inserting to std::cout (especially with a flush, i.e. as std::endl) can play hell during the benchmark stage, so I have removed that. I have also ensured that each benchmark minimally interferes with the others (by scoping each out), extracted the time for the copying operation (as it isn't valid to compare Vec::Vec() and Vec::Vec(const Vec&) - the two do different things). Most importantly, the methods are run many times to minimise statistical error and to try and iron out the impact of e.g. dynamic frequency scaling.

#include <vector>
#include <iostream>
#include <algorithm>
#include <functional>


class Vec {
public : 
    Vec () : val_ (std::vector<size_t> (1000, 0)) {}
    Vec (const Vec& v) : val_ (v.val_) {}
    //couple of const opposite methods
    void inc () {
        std::for_each (val_.begin (), val_.end (), [] (size_t&s) {++s;});
    }
    void dec () {
        std::for_each (val_.begin (), val_.end (), [] (size_t&s) {--s;});
    }    
    void next2const () const {
        Vec v (*this);
        v.inc();
        v.dec();
    }
    void next2pseudoconst() { //pseudo const method
        inc ();
        dec ();
    }
private :
    std::vector<size_t> val_;
};

template<typename T>
void crude_bench(T func, const size_t& count){
    clock_t t = clock();
    for(size_t i = 0; i < count; ++i){
        func();
    }
    double duration{(clock() - t) / (double)count};
    std::cout << duration << " ticks per run\n";
}

int main (int argc, char* argv []) {
    auto count = 100000ul;
    {
        Vec v;
        std::cout << "Copying: " << std::flush;
        crude_bench(
            std::bind([](Vec& v){ Vec other(v); }, v),
            count
        );
    }
    {
        Vec v;
        std::cout << "next2const: " << std::flush;
        crude_bench(
            std::bind(&Vec::next2const, v),
            count                
        );
    }
    {
        Vec v;
        std::cout << "next2pseudoconst: " << std::flush;
        crude_bench(
            std::bind(&Vec::next2pseudoconst, v),
            count
        );
    }
    return 0;
}

Without any optimisation on GCC, what I get is:

Copying: 0.31656 ticks per run
next2const: 18.2984 ticks per run
next2pseudoconst: 18.0145 ticks per run

Note that the next2const duration minus the Copying duration (which is necessary to compare the two) is less than the next2pseudoconst duration - but not by any meaningful amount. There's a good reason for that - const doesn't perform any kind of magic here. It wouldn't make sense if it did, although the Vec method is const, it still creates another Vec and performs a non-const operation upon THAT.

Just as a sidenote as to the importance of running benchmarks over many iterations. I have run the above benchmark over iterations increasing in powers of 2, and charted the ticks per iteration for GCC7.3.1 unoptimised and GCC7.3.1 with -O3. I'm not sure if I can attach the charts to the group via email attachment, so I have uploaded them here: https://imgur.com/a/Mj2P6 - the two lines correspond with ticks(next2pseudoconst) and ticks(next2const)-ticks(copying) - You can see the negative tick count of (next2const - copying) in the first iteration, which should go some way to tell you how unreliable a single run benchmark is.

Regards,
Jake Arkinstall

Richard Hodges

unread,
Feb 22, 2018, 2:08:41 AM2/22/18
to std-pr...@isocpp.org
On Thu, 2018-02-22 at 01:13 +0000, Jake Arkinstall wrote:
> Further to my previous reply, I have written a rough version which
> should be a bit more reliable (it isn't perfect, but should suffice
> for the purpose of demonstration).


C++ already provides the mutable keyword. The Vec class in question can
be written in terms of it:

class Vec {
public :
Vec () : val_ (std::vector<size_t> (1000, 0)) {}

// copy constructor is un-necessary. The compiler builds a correct
// one.

//actual const method
void next2() const {
inc ();
dec ();
}

// these are now const
void inc () const {
std::for_each (val_.begin (), val_.end (),
[] (size_t&s) {++s;});
}

void dec () const {
std::for_each (val_.begin (), val_.end (),
[] (size_t&s) {--s;});
}

private :
// mutable members can be modified when the object is accessed
// through a const reference
mutable std::vector<size_t> val_;
};
> On Wed, Feb 21, 2018 at 11:35 PM, Jake Arkinstall <jake.arkinstall@gm
signature.asc

Toby Allsopp

unread,
Feb 22, 2018, 4:19:16 AM2/22/18
to ISO C++ Standard - Future Proposals
On Thursday, 22 February 2018 14:13:50 UTC+13, Jake Arkinstall wrote:
Further to my previous reply, I have written a rough version which should be a bit more reliable (it isn't perfect, but should suffice for the purpose of demonstration).


In the const version you don't need to do the decrement operation so I commented it out. The const version is a lot faster than the pseudo-const version with that change.

Cheers,
Toby.

Jérôme Saint-Martin

unread,
Feb 22, 2018, 5:02:24 AM2/22/18
to std-pr...@isocpp.org
Dec was not in const version.

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

Jake Arkinstall

unread,
Feb 22, 2018, 8:30:22 AM2/22/18
to std-pr...@isocpp.org
Of course. But you cannot make claims about speed benefits if you dont make the two do the same thing - at that point the difference is arbitrary; it depends on the exact case you're dealing with. This is why the reversal is in there, because you had claims about speed benefits of pseudoconst that don't hold up.

My point was too break it down and show you that they're doing exactly the same thing except that one copies first. Of course, if the const version only needs to do half of the processing then it's just down to being efficiently copyable.

Message has been deleted

Iván Sanz

unread,
Feb 22, 2018, 8:41:21 AM2/22/18
to ISO C++ Standard - Future Proposals
what if a pseudoconst method isnt noexcept? allowing them would introduce tons of undefined behaviors

Jake Arkinstall

unread,
Feb 22, 2018, 8:44:52 AM2/22/18
to std-pr...@isocpp.org
It's better to have the method as non-const and deal with the consequences, if possible, than to make a vital class data member mutable. The former does not trick the user into creating unwanted side-effects, but the latter can. The former is honest, the latter is a trickster.

There's certainly a place for mutable, but not here IMO.


--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

Jérôme Saint-Martin

unread,
Feb 22, 2018, 3:30:18 PM2/22/18
to std-pr...@isocpp.org
A pseudo const method is a method who calls a method A at its beginning, few const methods, and then the reverse method A

Jérôme Saint-Martin

unread,
Feb 22, 2018, 3:36:43 PM2/22/18
to std-pr...@isocpp.org
constance is exists comparing end object to begin object, but not every calls are const.

Jake Arkinstall

unread,
Feb 22, 2018, 3:50:42 PM2/22/18
to std-pr...@isocpp.org
But again, the two main issues are that it is not const during the duration of the method, and the compiler will not be able to determine if the final call truly is the reverse of A.

Perhaps if there were a way of creating a reversible operation in a self-contained manner, I'd be more confident. An example of this would be a library that allows one to define an algorithm in such a manner as the compiler is able to reverse it (with certain assumptions about various undefined behaviours, maybe; remember that incrementing signed integer types beyond their max, or decrementing them from zero, is undefined behaviour and as such may not be valid on all systems).

I would take great interest, albeit academic, in this kind of approach. It would likely need a list of reversible instructions, for which the reverse can be determined (without necessarily relying on operator- being the reverse of operator+, because that is user defined for custom classes and they have every possibility of not being opposites). The list is iterated forwards with the initial operations, then iterated backwards with the reverse operations, to undo it.

Without such an approach, we lose all confidence in pseudo-constness from the POV of the compiler. I still don't see what benefit pseudo-const would provide, though. There is no optimisation benefit, and its use in a threaded application is limited.

Note also that the same effect is possible by creating a view on the class, allowing you to be const without copying anything.

Nicol Bolas

unread,
Feb 22, 2018, 4:03:29 PM2/22/18
to ISO C++ Standard - Future Proposals
On Thursday, February 22, 2018 at 3:30:18 PM UTC-5, Jérôme Saint-Martin wrote:
A pseudo const method is a method who calls a method A at its beginning, few const methods, and then the reverse method A

OK, but why does this concept need to be part of the type system? I can't think of a lot of circumstances where this would be genuinely useful. And certainly not useful enough to become part of the language.

And how would the compiler verify that A and ~A would be called? Consider `const`. If you write a method that takes a parameter as `const`, then you cannot do non-`const` things to it without an explicit cast. But with this "pseudoconst" business, the compiler has no way to verify that you've implemented your "pseudoconst" method correctly.

And if the compiler can't verify it, why is it part of the type system?

Thiago Macieira

unread,
Feb 22, 2018, 4:35:26 PM2/22/18
to std-pr...@isocpp.org
On Thursday, 22 February 2018 12:30:15 PST Jérôme Saint-Martin wrote:
> A pseudo const method is a method who calls a method A at its beginning,
> few const methods, and then the reverse method A

In other words, a method that is subject to the ABA problem. It's clearly not
thread-safe.

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center



jsaintm...@gmail.com

unread,
Feb 22, 2018, 11:54:41 PM2/22/18
to ISO C++ Standard - Future Proposals


On Thursday, February 22, 2018 at 12:00:10 AM UTC+1, jsaintm...@gmail.com wrote:

What do you think about a pseudoconst qualifier ? This qualifier would allow a const method to use non const methods, but leaves the object equal to what it was when the method was called.
The pseudoconst method can be checked by the compiler this way, the structure of the pseudo const methods must be : calls to methods A, const calls, reverse methods A.
Example :

    #include <vector>


    class Vec {
    public :
      Vec () : val_ (std::vector<size_t> (1000, 0)) {}
      Vec (const Vec& v) : val_ (v.val_) {}
      ~Vec () {}

      //couple of const opposite methods
      void inc () {
        std::for_each (val_.begin (), val_.end (), [] (size_t&s) {++s;});
      }
      void dec () {
        std::for_each (val_.begin (), val_.end (), [] (size_t&s) {--s;});
      }

      void write2 (std::ostream& os) const {
        os << "val_ [2] == " << val_ [2] << std::endl;
      }
      void write_next2const (std::ostream& os) const {
        Vec v (*this);
        v.inc ();
        v.write2 (os);
      }
      void write_next2pseudoconst (std::ostream& os) { //pseudo const method
        inc ();
        write2 (os);
        dec ();
      }
    //private :
      std::vector<size_t> val_;
    };

The test program :


    int main (int argc, char* argv []) {
      size_t duration (0);
      clock_t t (clock ());
      Vec v;
      v.write_next2const (std::cout);
      duration = (clock() - t);
      std::cout << "write_next2const took " << duration << " ticks" << std::endl;
      t = clock ();
      v.write_next2pseudoconst (std::cout);
      duration = (clock() - t);
      std::cout << "write_next2pseudoconst took " << duration << " ticks" << std::endl;
      return 0;
    }

shows that pseudoconst method is much faster :

    val_ [2] == 1
    write_next2const took 124 ticks
    val_ [2] == 1
    write_next2pseudoconst took 3 ticks

but the second could not be declared const though leaving the objects unchanged.
What about "pseudoconst" qualifier in next compilers ?


Jerome Saint-Martin,
Montrouge, France



jsaintm...@gmail.com

unread,
Feb 23, 2018, 12:04:35 AM2/23/18
to ISO C++ Standard - Future Proposals
(this was the original message on stackoverflow.com, it was voted off topic there, so I copied contents here)


jsaintm...@gmail.com

unread,
Feb 23, 2018, 12:19:01 AM2/23/18
to ISO C++ Standard - Future Proposals
The compiler may check the structure of a pseudo const is such :
-calls to methods A
-const or pseudo const calls
-calls to reverse methods A, in reverse
In the declaration of reverse methods, you may write the prototype of first method, then the prototype of the reverse method, appended with "reverse" + prototype of the original method

Nicol Bolas

unread,
Feb 23, 2018, 2:05:34 AM2/23/18
to ISO C++ Standard - Future Proposals
On Friday, February 23, 2018 at 12:19:01 AM UTC-5, jsaintm...@gmail.com wrote:
The compiler may check the structure of a pseudo const is such :
-calls to methods A
-const or pseudo const calls
-calls to reverse methods A, in reverse
In the declaration of reverse methods, you may write the prototype of first method, then the prototype of the reverse method, appended with "reverse" + prototype of the original method

That guarantees squat. Nothing guarantees that `A` and `reverse_A` are actually proper inverse operations. Nothing guarantees that they can operate recursively (that you can call a "pseudoconst" method that performs its own `A` and `reverse_A`).

Again, compare that to `const`. Outside of `const_cast` or C-style casts, you cannot violate the restrictions of `const`. There's no point where the compiler goes, "well, I'll trust that the user isn't doing the wrong thing," except for the parts that explicitly call that out with specific syntax.

And what of control flow that is more complex than what you describe? Can you put the `reverse_A` call into a lambda that gets called as part of some forwarding operation before the termination of your "pseudo-const" function? If not, why not?

And lastly, you've not provided a single example of where someone would need to actually do this. And I don't mean the artificial example you gave on SO; I mean something real that you would actually need to do.

Message has been deleted
Message has been deleted

Nicol Bolas

unread,
Feb 23, 2018, 3:27:12 AM2/23/18
to ISO C++ Standard - Future Proposals
On Friday, February 23, 2018 at 3:17:08 AM UTC-5, jsaintm...@gmail.com wrote:

1) checking method A and reverse_A are actually reverse would be the work of the developer and debogger

So why is it part of the type system? If the compiler can't actually promise anything, why is it there?

2) I don't know
3) example, no sources. But in the compression tool I am trying to create, I am applying an elementary compression many times. To choose the best elementary compression, I would like to try it without copying the whole file, so be able to revert it, and my method of elementary compression could be pseudo const

`const` serves needs. It allows you to specify that an interface will not modify the object, so that users can choose to use that interface if they wish to leave the object unmodified. It allows the compiler to ensure that interfaces that are specified to leave the object unmodified will actually do so, This allows users to trust that `const` means `const`.

`const` as a type qualifier also permits the possibility of constant forwarding, the way that a `const vector<int>`'s begin/end functions return `const_iterator`s, due to overload resolution. And so forth.

You say that such a function can be declared "pseudo-const". OK sure. But what would that gain users? What would you as the caller of such a function be able to do which you could not before? And why is it important to be able to do so?

Jérôme Saint-Martin

unread,
Feb 23, 2018, 3:58:37 AM2/23/18
to std-pr...@isocpp.org
pseudo const Is an indication to users : "the method should not change the object".

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

jsaintm...@gmail.com

unread,
Feb 23, 2018, 4:33:20 AM2/23/18
to ISO C++ Standard - Future Proposals
"...but thats not guaranteed by compiler"

jsaintm...@gmail.com

unread,
Feb 23, 2018, 5:08:02 AM2/23/18
to ISO C++ Standard - Future Proposals

Or  the exact indication to the user could be "this method should leave the object unchanged, but that was not completedly checked by compiler"

floria...@gmail.com

unread,
Feb 23, 2018, 5:36:39 AM2/23/18
to ISO C++ Standard - Future Proposals
Then, how is it different to have it written in the documentation of the interface?

If it's only to tell the user without any of checking or enforcing it is used right, then there is no point to have a language feature for that.

BTW, when (if ever) we have contract based programming, you will be able to do exactly that (and much more) directly.
I don't remember any syntax, but this would be a post-condition similar to: *previous_this == *this

Richard Hodges

unread,
Feb 23, 2018, 5:38:23 AM2/23/18
to std-pr...@isocpp.org
On Fri, 2018-02-23 at 09:58 +0100, Jérôme Saint-Martin wrote:
pseudo const Is an indication to users : "the method should not change the object".

This is the indication/guarantee that const already provides. The pseudoness of the guarantee is an implementation detail, achieved through the mutable keyword which exists for this very purpose.

I am unconvinced of the merits of this proposal.

My reasoning is as follows:

1. The concept of psudo-constness already exists and is already trivially expressible in the language. 

2. Advertising a method as "pseudo const" merely leaks implementation information to the caller, which is an anti-pattern.

R
signature.asc

jsaintm...@gmail.com

unread,
Feb 23, 2018, 5:59:33 AM2/23/18
to ISO C++ Standard - Future Proposals
The interest of a pseudo const method is to comfort the user and still have good performance : "this method should normally be const, but for reasons of performance, it's just pseudo const. Use at your peril"

Jake Arkinstall

unread,
Feb 23, 2018, 6:28:34 AM2/23/18
to std-pr...@isocpp.org
On 23 Feb 2018 10:59, <jsaintm...@gmail.com> wrote:
for reasons of performance, it's just pseudo const

[Citation Needed]

You're going to have to give an example where this is actually the case. Because for the example you provided (but benchmarked properly, not a worthless single iteration run) the pseudo const method provides no gains. In fact, the const alternative when you don't have to run undo-A is faster.

And again, it could be even faster if you made a view class because you wouldn't need to copy the entire object - in your example you could create a "size_t op(const size_t&)" and pass that to the view, only executing op when a value is actually needed, and this would be executed only once. There are situations where this isn't as efficient as constructing a new object, but that still ends up as more efficient than modifying the original data twice in any situation where it actually matters.

Jake Arkinstall

unread,
Feb 23, 2018, 7:27:03 AM2/23/18
to std-pr...@isocpp.org
On 23 Feb 2018 08:58, "Jérôme Saint-Martin" <jsaintm...@gmail.com> wrote:
pseudo const Is an indication to users : "the method should not change the object". 

Const is an guarantee to users: the method does not change the object. A pseudoconst method would also have to guarantee this or the proposal wouldn't be useful at all. So, once again, come up with a way of guranteeing that a modification is reversed. A "reversible" or similar keyword would be more interesting. All it needs to do is only permit a series of commands and functions that are themselves reversible (ones that lose no information, so type limitations must be considered - see, for example, http://cpp.sh/9fet5 and http://cpp.sh/9redh ), in the same way that const only permits use of operations that are also const. Then you need something built into the language that does the reversing.

This could be useful in a variety of situations. One of which would be an enforceable pseudoconst.

jsaintm...@gmail.com

unread,
Feb 23, 2018, 9:38:20 AM2/23/18
to ISO C++ Standard - Future Proposals
"reversible" sounds good to me. Attached is the small example I wrote on stack and deleted.
Reversible.cpp

Ville Voutilainen

unread,
Feb 23, 2018, 9:44:40 AM2/23/18
to ISO C++ Standard - Future Proposals
On 23 February 2018 at 16:38, <jsaintm...@gmail.com> wrote:
> "reversible" sounds good to me. Attached is the small example I wrote on
> stack and deleted.

I don't know what sort of a reversible example that is, because it's
not exception-safe. Is that intentional?

jsaintm...@gmail.com

unread,
Feb 23, 2018, 10:02:05 AM2/23/18
to ISO C++ Standard - Future Proposals
nope , was just a first example, showing reversible is faster than const

Jake Arkinstall

unread,
Feb 23, 2018, 10:16:40 AM2/23/18
to std-pr...@isocpp.org
If you require that the operation is reversible, you probably shouldn't trust the source blindly.

The idea is more along the lines of

void inc(T x) reversible{
    // ....
}

void dec(T x) reversible{
    reverse(inc)(x);
}

Where we can assume reverse(reverse(f)) is reduced to f, for some method f. The reversible keyword informs the compiler that the method should be split into statements, and each such statement must be reversible (i.e. is a method that has the 'reversible' keyword). Each statement is reversed and iterated back-to-front when the reverse() method (or some arbitrary syntax) is called on a method. In other words, the user doesn't explicitly define the reverse (users cannot be trusted, which is exactly why const enforces const). This propogates all the way down to fundamental operations, so at some point the reverse of those operations need to be defined - but by the language, not by the user.

Then RAII could be exploited to ensure that a pseudoconst method really undoes the initial operation:

void nonconst_example() pseudoconst{
    Transformer t(this, inc); // transformation is performed
    // do what you as long as it's const
} // t::~t() is called, undoing the transformation.

It's a bit of a strange idea, but it's the only way that I can think of that allows pseudoconst to be reliable.

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

Jake Arkinstall

unread,
Feb 23, 2018, 10:28:13 AM2/23/18
to std-pr...@isocpp.org
Your example shows no such thing. I have said it calmly enough times, so I'll step it up a notch: Stop doing benchmarks along a single run - they are absolutely pointless, unreliable, usually wrong (e.g DVFS), and overall worthless. As a community we can't take your ideas seriously if you don't respect that, and we won't take your ideas seriously if you don't learn anything when we show you a flaw in your approach.

Take the following as an example of why this is dumb:

#include <iostream>

int main () {

  size_t duration (0);
  clock_t t (clock ());
  std::cout << "Hello" << std::endl;

  duration = (clock() - t);
  std::cout << "Cout took " << duration << " ticks" << std::endl;

  t = clock ();
  std::cout << "Hello" << std::endl;

  duration = (clock() - t);
  std::cout << "Cout took " << duration << " ticks" << std::endl;
  return 0;
}

Output:
Hello
Cout took 30 ticks
Hello
Cout took 2 ticks

No one in their right mind would say that this benchmark proves anything.

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

jsaintm...@gmail.com

unread,
Feb 23, 2018, 10:35:33 AM2/23/18
to ISO C++ Standard - Future Proposals
what tool shall I use for benchmarking ?
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.

To post to this group, send email to std-pr...@isocpp.org.

Ville Voutilainen

unread,
Feb 23, 2018, 10:37:48 AM2/23/18
to ISO C++ Standard - Future Proposals
On 23 February 2018 at 17:28, Jake Arkinstall <jake.ar...@gmail.com> wrote:
> Your example shows no such thing. I have said it calmly enough times, so


That example shows that modifying values in an existing buffer is
faster than re-allocating a new buffer, copying data over to it,
and releasing that buffer. How that leads into a need to have a
pseudoconst in C++ is beyond me.

We have a Transactional Memory TS. It shows us that there are limits
to what operations can be transactional.

Jake Arkinstall

unread,
Feb 23, 2018, 10:50:19 AM2/23/18
to std-pr...@isocpp.org
On Fri, Feb 23, 2018 at 3:37 PM, Ville Voutilainen <ville.vo...@gmail.com> wrote:
That example shows that modifying values in an existing buffer is
faster than re-allocating a new buffer, copying data over to it,
and releasing that buffer.

It's not faster, though. See Toby's benchmark on reply 6: http://quick-bench.com/3CuEaO513mfIZmS6Ca1rrC_DnII

I can see some use for a pseudoconst, in that the user is aware that the object will be the same at the end as the beginning, but that it isn't thread safe. There are no performance benefits but exposing and enforcing a contract could have its perks.

Ville Voutilainen

unread,
Feb 23, 2018, 10:55:30 AM2/23/18
to ISO C++ Standard - Future Proposals
On 23 February 2018 at 17:50, Jake Arkinstall <jake.ar...@gmail.com> wrote:
>> That example shows that modifying values in an existing buffer is
>> faster than re-allocating a new buffer, copying data over to it,
>> and releasing that buffer.
>
>
> It's not faster, though. See Toby's benchmark on reply 6:
> http://quick-bench.com/3CuEaO513mfIZmS6Ca1rrC_DnII

Hah, indeed, the mutation of the copy doesn't need to be reversed.

> I can see some use for a pseudoconst, in that the user is aware that the
> object will be the same at the end as the beginning, but that it isn't
> thread safe. There are no performance benefits but exposing and enforcing a
> contract could have its perks.

I fail to see how that's different from traditional exception safety.
We don't have a keyword or a test for that,
though. There was a time when it was suggested that it would make
sense to add 'basic' and 'strong' to the
set of one that currently contains 'noexcept', but how that should be
used is something that never got enough
motivation for that idea to make progress.

Thiago Macieira

unread,
Feb 23, 2018, 11:13:49 AM2/23/18
to std-pr...@isocpp.org
On Friday, 23 February 2018 07:35:32 PST jsaintm...@gmail.com wrote:
> what tool shall I use for benchmarking ?

perf, over a run of the benchmark of at least a full second an otherwise-idle
system. Preferably, more. And you should do some statistics over a couple of
runs.

jsaintm...@gmail.com

unread,
Feb 23, 2018, 1:14:44 PM2/23/18
to ISO C++ Standard - Future Proposals
I would not have wirte it your way, but this way (reversible and reverse part of declaration) but like this :
#include <vector>

class Vec {
public : 
  Vec () : val_ (std::vector<size_t> (1000, 0)) {}
  Vec (const Vec& v) : val_ (v.val_) {}
  ~Vec () {}

  //couple of reverse methods

  void inc () {
    std::for_each (val_.begin (), val_.end (), [] (size_t&s) {++s;});
  }

  void dec () reverse inc () {

    std::for_each (val_.begin (), val_.end (), [] (size_t&s) {--s;});
  }

  void write2 (std::ostream& os) const {
    os << "val_ [2] == " << val_ [2] << std::endl;
  }
  void write_next2const (std::ostream& os) const {
    Vec v (*this);
    v.inc ();
    v.write2 (os);
  }

  void write_next2reversible (std::ostream& os) reversible  {

    inc ();
    write2 (os);
    dec ();
  }
//private :
  std::vector<size_t> val_;
};

and about the "thread safe" problem, fel free to insert a muex

Jake Arkinstall

unread,
Feb 23, 2018, 2:36:48 PM2/23/18
to std-pr...@isocpp.org


On 23 Feb 2018 18:14, <jsaintm...@gmail.com> wrote:
I would not have wirte it your way, but this way (reversible and reverse part of declaration) but like this :
#include <vector>
class Vec { public : Vec () : val_ (std::vector<size_t> (1000, 0)) {} Vec (const Vec& v) : val_ (v.val_) {}
~Vec () {} //couple of reverse methods
void inc () { std::for_each (val_.begin (), val_.end (), [] (size_t&s) {++s;}); }
void dec () reverse inc () {
std::for_each (val_.begin (), val_.end (), [] (size_t&s) {--s;}); }

Then you lose the guarantee that the reversal is actually valid and the whole thing is pointless.

jsaintm...@gmail.com

unread,
Feb 23, 2018, 3:01:53 PM2/23/18
to ISO C++ Standard - Future Proposals
reversible is for the user. You trust compiler, why would not he trust you ?

Jake Arkinstall

unread,
Feb 23, 2018, 3:39:34 PM2/23/18
to std-pr...@isocpp.org
"A common mistake that people make when trying to design something completely foolproof is to underestimate the ingenuity of complete fools" - Douglas Adams.

In my experience, this is very much the case. If something is worthy of being a part of the language, it should be so in a consistent manner. A keyword is worthless if not enforceable by the compiler, especially when you get newer users trying to get around what they think is a restriction in a provided API by breaking everything they can find.

On 23 Feb 2018 20:01, <jsaintm...@gmail.com> wrote:
reversible is for the user. You trust compiler, why would not he trust you ?


Then you lose the guarantee that the reversal is actually valid and the whole thing is pointless.

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

jsaintm...@gmail.com

unread,
Feb 24, 2018, 3:54:56 AM2/24/18
to ISO C++ Standard - Future Proposals
But you may avoid a great number of fools -if not all- placing a sign "fools : no entry".

On Friday, February 23, 2018 at 9:39:34 PM UTC+1, Jake Arkinstall wrote:
"A common mistake that people make when trying to design something completely foolproof is to underestimate the ingenuity of complete fools" - Douglas Adams.

In my experience, this is very much the case. If something is worthy of being a part of the language, it should be so in a consistent manner. A keyword is worthless if not enforceable by the compiler, especially when you get newer users trying to get around what they think is a restriction in a provided API by breaking everything they can find.
On 23 Feb 2018 20:01, <jsaintm...@gmail.com> wrote:
reversible is for the user. You trust compiler, why would not he trust you ?


Then you lose the guarantee that the reversal is actually valid and the whole thing is pointless.

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.

Jake Arkinstall

unread,
Feb 24, 2018, 4:18:07 AM2/24/18
to std-pr...@isocpp.org
On 24 Feb 2018 08:54, <jsaintm...@gmail.com> wrote:
But you may avoid a great number of fools -if not all- placing a sign "fools : no entry". 

No. They see it as an invitation.

If a keyword can't be validated by the compiler, there's no point in it. You can get pseudoconst without any effort by simply not declaring it const. But if you want to introduce a new concept it needs to be enforceable or it isn't worth the work that goes into the change.

If what you want is a sign on the door, you can do that with comments. If you want an iris-scanner and finger print reader combination deadbolt, three armed security guards, Lego blocks on the floor, a bobcat, and a moat containing sharks with lasers, that's when you need a language modification.

jsaintm...@gmail.com

unread,
Feb 24, 2018, 11:48:42 PM2/24/18
to ISO C++ Standard - Future Proposals
you may be right I ll do it by myself. Do you have an address where I may download g++ sources ?
Message has been deleted
Message has been deleted
Message has been deleted
Message has been deleted
Message has been deleted
Message has been deleted

jsaintm...@gmail.com

unread,
Feb 26, 2018, 12:11:43 AM2/26/18
to ISO C++ Standard - Future Proposals
all you need to do is to add the reverse and reversible qualifiers for basic types


On Saturday, February 24, 2018 at 10:18:07 AM UTC+1, Jake Arkinstall wrote:
Message has been deleted

jsaintm...@gmail.com

unread,
Feb 26, 2018, 1:19:14 AM2/26/18
to ISO C++ Standard - Future Proposals
... and a few mutex...

jsaintm...@gmail.com

unread,
Feb 28, 2018, 3:30:41 PM2/28/18
to ISO C++ Standard - Future Proposals
Another use of pseudoconst / reversible would allow to use hint to find an element in map within a pseudo const method. As far as I know,
m.insert (ihint, p) exists,
but not map.find (ihint, p)

With peudo const, you could write your pseudoconst find,  like this, if you are certain -1 is not a second existing in the map, and map.erase (i) was defined as the reverse of insert (i) :

std::map<size_t, size_t>::iterator find (std::map<size_t, size_t>& m, std::pair<size_t, size_t>& p)  reversible {
  std::pair<size_t, size_t> p2 .(p.first, (size_t) -1);
  m.insert (ihint, p);
  if (ihint->second == (size_t) -1) {
    m.erase (ihint);
    ihint = m.end ();
  }
  return ihint;
}
 


"
On Friday, February 23, 2018 at 1:27:03 PM UTC+1, Jake Arkinstall wrote:
On 23 Feb 2018 08:58, "Jérôme Saint-Martin" <jsaintm...@gmail.com> wrote:
pseudo const Is an indication to users : "the method should not change the object". 

Const is an guarantee to users: the method does not change the object. A pseudoconst method would also have to guarantee this or the proposal wouldn't be useful at all. So, once again, come up with a way of guranteeing that a modification is reversed. A "reversible" or similar keyword would be more interesting. All it needs to do is only permit a series of commands and functions that are themselves reversible (ones that lose no information, so type limitations must be considered - see, for example, http://cpp.sh/9fet5 and http://cpp.sh/9redh ), in the same way that const only permits use of operations that are also const. Then you need something built into the language that does the reversing.

This could be useful in a variety of situations. One of which would be an enforceable pseudoconst.
"

jsaintm...@gmail.com

unread,
Feb 28, 2018, 3:33:24 PM2/28/18
to ISO C++ Standard - Future Proposals
sorry, ihint = m.insert (ihint, p2) instead od m.insert (ihint, p)

Nicol Bolas

unread,
Feb 28, 2018, 4:38:04 PM2/28/18
to ISO C++ Standard - Future Proposals
On Wednesday, February 28, 2018 at 3:30:41 PM UTC-5, jsaintm...@gmail.com wrote:
Another use of pseudoconst / reversible would allow to use hint to find an element in map within a pseudo const method. As far as I know,
m.insert (ihint, p) exists,
but not map.find (ihint, p)

I fail to see how pseudoconst would be an essential element to such a function.

With peudo const, you could write your pseudoconst find,  like this, if you are certain -1 is not a second existing in the map, and map.erase (i) was defined as the reverse of insert (i) :

But why would you want to? I don't see any performance improvement from this function. Because if you fail to find the item, then you have to go through the process of creating the element only to destroy it. Why would you do that? Creating elements requires allocating memory, which is pretty expensive.

jsaintm...@gmail.com

unread,
Mar 1, 2018, 2:15:49 AM3/1/18
to ISO C++ Standard - Future Proposals
In this function, I am using insert instead of find, because it allows hint. Insert is not const, but the element inserted is be erased

Richard Hodges

unread,
Mar 1, 2018, 4:57:41 AM3/1/18
to std-pr...@isocpp.org
Forgive me but I am still struggling to see the difference between const and pseudo-const.

A long long time ago, when I used to write embedded C, "const" meant "it's in ROM. It won't change even if you write to it". (they were simple proprietary systems with no MMU and I wrote custom linker scripts and c runtime startup code).

Then c++ came along and const meant "This method will not change the logical state of the object". Which it seems to me is precisely what you are intending to communicate with "pesudo-const". I understand that your intention is to communicate that there will be some temporary modification of state, but there are 2 problems with this:

1. Because the c++ memory model is single-threaded, the fact that there have been internal state changes during the call is irrelevant. Those state changes are not observable to the caller, so for all intents and purposes, "pseudo-const" and "const" as it currently stand mean precisely the same thing.

2. Telling the caller that there will be some temporary, invisible, un-observable state change during a call which is for him/her atomic, is simply leaking information that the caller has no way of using. It conveys no more useful information than "const". There is therefore no use-case for it.

In reality, the "const" keyword when applied to a method, already means "pseudo-const". This is because we have mutable members.

There might be an argument for a new attribute (say, [[immutable]]), which would indicate that the value is utterly immutable for the life of the program, and that a method marked as such will always return the same value (allowing the compiler to cache the results of calls).

Although we kind-of have that with constexpr already.

Summary:

* The existing keyword "const" really means "psuedo-const".

* const objects in c++ are not *really* constants.

* const methods are already allowed to modify internal state, but good manners dictates that these internal state changes are not observable to client code.

 

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.

To post to this group, send email to std-pr...@isocpp.org.
Message has been deleted
Message has been deleted
Message has been deleted

jsaintm...@gmail.com

unread,
Mar 1, 2018, 9:39:22 AM3/1/18
to ISO C++ Standard - Future Proposals

"const " and "pseudo const" methods are differents. A more simple example :
class A {
public :
  A () : val_ (0) {}
  A (const A& a) : val_ (a.val_) {}
  ~A () {}
  //couple of const opposite methods
  void inc () const {
    val_++;
  }
  void dec () const {
    val_--;
  }
  void write (std::ostream& os) const {
    os << val_;
  }
  void write_nextconst (std::ostream& os) const { //const method
    inc ();
    write (os);
  }
  void write_nextpseudoconst (std::ostream& os) const { //pseudo const method
    inc ();
    write (os);
    dec ();
  }
  mutable size_t val_;
};

int main (int argc, char* argv []) {
  size_t duration (0);
  A v;

  std::cout << "\ttest write_nextconst" << std::endl;
  std::cout << "A.val_ before : ";
  v.write (std::cout);
  std::cout << ", next val_ :";
  v.write_nextconst (std::cout);
  std::cout << ", A.val_ after : ";
  v.write (std::cout);
  std::cout << std::endl;

  std::cout << "\ttest write_nextpseudoconst" << std::endl;
  A v2;
  std::cout << "A.val_ before : ";
  v2.write (std::cout);
  std::cout << ", pseudo const nextval_ : ";
  v2.write_nextpseudoconst (std::cout);
  std::cout << ", after :";
  v2.write (std::cout);
  std::cout << std::endl;
  return 0;
}

results :
        test write_nextconst
A.val_ before : 0, next val_ :1, A.val_ after : 1
        test write_nextpseudoconst
A.val_ before : 0, pseudo const nextval_ : 1, after :0

method write_nextconst changed the mutable member val_  permenently.
pseudo const write_next_pseudoconst gives the same result, but the object at the end is the same as the object at the beginning, because the pseudoconst method called in this order methods M, const methods, reverse methods M in reverse order.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.

To post to this group, send email to std-pr...@isocpp.org.

Richard Hodges

unread,
Mar 1, 2018, 9:41:04 AM3/1/18
to std-pr...@isocpp.org


On 1 March 2018 at 14:16, <jsaintm...@gmail.com> wrote:
A "const" does not mean "pseudo const ". A more simple example :


class A {
public :
  A () : val_ (0) {}
  A (const A& a) : val_ (a.val_) {}
  ~A () {}
  //couple of reverse methods

  void inc () const {
    val_++;
  }
  void dec () const {
    val_--;
  }
  void write (std::ostream& os) const {
    os << val_;
  }

 
  void write_nextconst (std::ostream& os) const { //const method
    inc ();
    write (os);
  }

I understand what you are illustrating. The problem here is that the method "write_nextconst()" is lying. It is advertised as const ("I guarantee that I will not mutate the logical state of the object") but has, in fact, left the logical observable state mutated. This is certainly an anti-pattern and very likely to be a logic error.

I would agree that some compiler help in identifying this error would be welcome, e.g.:

"warning: main.cpp:122 : const method modifies the observable state of the object."

Quite how a compiler maintainer would implement that without giving false positives, I cannot answer at the present time. 

We have some protection, in that val_ must be marked "mutable", which at least gives a maintainer a warning to be wary when modifying const methods of the class.

A reliable way to implement a software transaction/reversible process is through the constructor/destructor of a transaction object. Boost.ScopeExit is one attempt at simplifying such a tool.

The bugbear in this kind of code is exception handling, so for code to be truly reversible it must be exception-free and/or use objects that give the strong guarantee.

In summary, the tools already exist. They (at least in my experience) are used so infrequently as to beg the question, "why bake them into the language?". If you can offer examples of common code that would benefit from being refactored to use such reversible mutations, you'll start to build a less sceptical audience.

R

 
  void write_nextpseudoconst (std::ostream& os) const { //pseudo const method
    inc ();
    write (os);
    dec ();
  }
  mutable size_t val_;
};

int main (int argc, char* argv []) {
  size_t duration (0);
  A v;

  std::cout << "\ttest write_nextconst" << std::endl;
  std::cout << "before : ";
  v.write (std::cout);
  std::cout << " next A :";
  v.write_nextconst (std::cout);
  std::cout << " after : ";

  v.write (std::cout);
  std::cout << std::endl;

  std::cout << "\ttest write_nextpseudoconst" << std::endl;
  A v2;
  std::cout << "before : ";
  v2.write (std::cout);
  std::cout << " next A : ";
  v2.write_nextpseudoconst (std::cout);
  std::cout << " after :";

  v2.write (std::cout);
  std::cout << std::endl;
  return 0;
}

results :
        test1
before : 0 next A :1 after : 1
        test2
before : 0 next A : 1 after :0

        test write_nextconst
before : 0 next A :1 after : 1
        test write_nextpseudoconst
before : 0 next A : 1 after :0

Results are different :
The const method changed the mutable object permanently.
The future pseudo const method write_next leaves the object unchanged, even if it was changed during method. A oseudo const method shall call methods M, const methods, and reverse methods M in reverse order


On Thursday, March 1, 2018 at 10:57:41 AM UTC+1, Richard Hodges wrote:
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.

To post to this group, send email to std-pr...@isocpp.org.

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

Nicol Bolas

unread,
Mar 1, 2018, 10:07:49 AM3/1/18
to ISO C++ Standard - Future Proposals
On Thursday, March 1, 2018 at 2:15:49 AM UTC-5, jsaintm...@gmail.com wrote:
In this function, I am using insert instead of find, because it allows hint. Insert is not const, but the element inserted is be erased

Yes, I see that, but I don't understand why you want that. You haven't improved the performance of this operation. So what's the point?

I've yet to see an example of this "pseudoconst" idea that shows why someone would want to use it.

jsaintm...@gmail.com

unread,
Mar 1, 2018, 12:35:06 PM3/1/18
to ISO C++ Standard - Future Proposals
I want this because for I must choose between different methods, the one giving the best result. I need to try all methods without changing the object (with a guarantee of the compiler : const calls M, const or pseudo const calls, reverse M calls), and then choose the method

jsaintm...@gmail.com

unread,
Mar 1, 2018, 12:52:38 PM3/1/18
to ISO C++ Standard - Future Proposals
and then call the best method

Nicol Bolas

unread,
Mar 1, 2018, 1:08:32 PM3/1/18
to ISO C++ Standard - Future Proposals
On Thursday, March 1, 2018 at 12:35:06 PM UTC-5, jsaintm...@gmail.com wrote:
I want this because for I must choose between different methods, the one giving the best result. I need to try all methods without changing the object (with a guarantee of the compiler : const calls M, const or pseudo const calls, reverse M calls), and then choose the method

You keep repeating this and giving examples, none of which actually explain why this is necessary. The examples you provide only show that this is what you want to do, not why it needs to be done this way.

We know what you want. I don't understand why you think this is a necessary language feature. It seems to me that you can do it just fine as the standard currently is.

jsaintm...@gmail.com

unread,
Mar 1, 2018, 1:11:46 PM3/1/18
to ISO C++ Standard - Future Proposals
If you have several hundreds of possibilities to try, and the object is huge, it's faster to try each, cancel it and try the next one, than to make a copy of the whole object hundreds of times

Nicol Bolas

unread,
Mar 1, 2018, 1:24:46 PM3/1/18
to ISO C++ Standard - Future Proposals
On Thursday, March 1, 2018 at 1:11:46 PM UTC-5, jsaintm...@gmail.com wrote:
If you have several hundreds of possibilities to try, and the object is huge, it's faster to try each, cancel it and try the next one, than to make a copy of the whole object hundreds of times

Then do that. Your method is non-const. There's nothing in the language preventing you from doing this.

What you want is not nearly useful enough to be worthy of a language feature, even if the feature could be guaranteed by the compiler in some way.

Gašper Ažman

unread,
Mar 1, 2018, 1:26:14 PM3/1/18
to std-pr...@isocpp.org
I'm not sure if I understand - is there a scenario where you would provide both a const and a pseudoconst method on a class?

If it's just a matter of implementation, you know you can const-cast the this pointer into a non-const pointer so you can call mutable methods on it? The interface method would then bear a const signature while the implementation merely needs to ensure there can never be user-visible side effects.

I fail to grasp a context where having a type-system-understood third layer of constness that is never present in both flavors on a single object.

G

On Thu, Mar 1, 2018, 17:52 <jsaintm...@gmail.com> wrote:
and then call the best method

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

Nicol Bolas

unread,
Mar 1, 2018, 1:29:48 PM3/1/18
to ISO C++ Standard - Future Proposals


On Thursday, March 1, 2018 at 1:26:14 PM UTC-5, Gašper Ažman wrote:
I'm not sure if I understand - is there a scenario where you would provide both a const and a pseudoconst method on a class?

If it's just a matter of implementation, you know you can const-cast the this pointer into a non-const pointer so you can call mutable methods on it?

"Can" is a strong word. If the object itself was declared `const`, then you "can" not. Or rather you "can", but any mutable calls will provoke UB.

jsaintm...@gmail.com

unread,
Mar 1, 2018, 1:59:46 PM3/1/18
to ISO C++ Standard - Future Proposals
Thank you for your answer.

Gašper Ažman

unread,
Mar 1, 2018, 2:46:36 PM3/1/18
to std-pr...@isocpp.org
So the main issue is that some method of avoiding UB is needed but the method needs to advertise const.

Let's imagine for a second that we now have three layers in the type system - immutable (writing to memory is always UB), const (like now sans UB so casts permitted, can't be in ROM) and regular mutable.

Now we are forcing API designers to choose which type of const/immutable reference they will accept, since immutable objects cannot (ever!) downcast to const.

Option a) almost-always-immutable&:

This one is very much like the current const& thing. Unfortunately this means that things that are const& are basically useless in the immutable& world, since no library actually works with them - they all want immutable& for convenience's sake so that they can be used with immutable objects.

Option b) almost-always-const&:

In this world, immutable objects are basically useless, since they can't be used with the majority of libraries, which really just want to say "I'm not going to mutate this" - and they don't, so could have been taking immutable.

Option c) Everyone does their own thing
This one is a mix of above, which sucks even more.


All of these suck. This is why we don't have different meanings of const in the language. Introducing a third has always seemed like the cure is worse than the disease.



Basically, I don't know how to do this in a way that doesn't make huge problems for library authors within the typesystem. Perhaps we could define const better, and just say that modifying objects declared const is defined behavior as long as they haven't been put into ROM. Say we introduce something like

class Foo {
  int bar;
  void my_pseudoconst() const mutable {
    ++bar;
    --bar;
  }
};

which basically says "classes that have mutable methods can't be put into ROM and have defined const-cast for this semantics". I could do with something like that. I don't see how introducing another constness category would ever work, though.

G




--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.

To post to this group, send email to std-pr...@isocpp.org.

jsaintm...@gmail.com

unread,
Mar 2, 2018, 3:24:20 PM3/2/18
to ISO C++ Standard - Future Proposals
Yes suck yeah, . UB advertise ROM immutable suck...  || Brilliant remarks&, thanks

jsaintm...@gmail.com

unread,
Mar 2, 2018, 3:39:25 PM3/2/18
to ISO C++ Standard - Future Proposals
there might be other sites where you can write "suck" ? I did not thought this was for this


On Thursday, March 1, 2018 at 8:46:36 PM UTC+1, Gašper Ažman wrote:
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.

jsaintm...@gmail.com

unread,
Mar 2, 2018, 3:51:33 PM3/2/18
to ISO C++ Standard - Future Proposals
or is your vocabulary limited to "suck"

Jake Arkinstall

unread,
Mar 2, 2018, 4:03:05 PM3/2/18
to std-pr...@isocpp.org
There are plenty of more fitting four letter words, but their use would be frowned upon.

Suck is perfectly fine. Criticising another person's choice of words is not. Don't push your luck.

jsaintm...@gmail.com

unread,
Mar 2, 2018, 4:17:39 PM3/2/18
to ISO C++ Standard - Future Proposals
Go n buy a brain. Nicol Bolas understood. Ask him.

jsaintm...@gmail.com

unread,
Mar 2, 2018, 4:24:55 PM3/2/18
to ISO C++ Standard - Future Proposals
I dont like it. If this suit you, please, go for it, but without me

jsaintm...@gmail.com

unread,
Mar 2, 2018, 4:47:48 PM3/2/18
to ISO C++ Standard - Future Proposals
Even if there are more words in French than in English, I believed it was possible to male two sentences in English, without using "suck". I was mistaken. Sorry


On Friday, March 2, 2018 at 10:03:05 PM UTC+1, Jake Arkinstall wrote:

Gašper Ažman

unread,
Mar 3, 2018, 4:16:25 AM3/3/18
to std-pr...@isocpp.org
Jerome,


I'm sorry if I offended you by using emphatic (as opposed to graphic) language. I thought it would get the message of how unfortunate the situation I was outlining was. Instead, I achieved a fixation on a communication technique instead of the message, which was not my intention. I'd also like to avoid another one-liner storm that completely derailed the discussion.

I'd like to continue this discussion, so please, replace, in your mind, if you don't mind, the word "suck" with "not ideal", and address my concerns on a substantive level, like I tried to address yours.

In fact, I believe I suggested an alternative solution for your problem that avoids the type system issues that come with bifurcating the meaning of const, which has been actually refused several times, for the outlined reasons that I'm not the first to bring up (though perhaps in this discussion, my elucidation of them was the most direct).

The solution I suggested, if it was not obvious, is to, for classes that define a "const mutable" method, define (as opposed to UB) what a const-declared object of that type is allowed to be. For such classes (and classes containing them), even const-declared objects cannot be put into ROM, which means that const-cast of this is not UB for them. That should address your implementation concerns without bifurcating the type system, unless I'm misunderstanding something and you want to provide both a const  and a pseudoconst method on the same class, in which case, please elucidate why that would ever be useful, because I truly cannot think of an example.


Gašper



--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.

To post to this group, send email to std-pr...@isocpp.org.

jsaintm...@gmail.com

unread,
Mar 6, 2018, 8:38:20 AM3/6/18
to ISO C++ Standard - Future Proposals

got a partial answer to my own question, but it might interest some other coders : it's looks not possible to know what will be an object at next iteration in a const method, without copying it, but it is possible in a const method, without creating another object (just a copy to an existing one). const qualifier after a method that modifies static members static members is valid, and I am using this. This aspect of C++ may not be known enough :

  class Integer {
  public :
    Integer (size_t s) : val_ (s) {}
    ~Integer () {}
    Integer (const Integer& i) : val_ (i.val_) {}
    Integer& operator = (const Integer& i) {val_ = i.val_; return *this;}
    bool operator == (const Integer& i) {return (val_ == i.val_?true:false);}
    bool operator != (const Integer& i) {return !operator == (i);}

    void write (std::ostream& os) const {
      os << val_;
    }
    void write_next (std::ostream& os) const {
      me_stat = *this;
      me_stat++;
      me_stat.write (os);
    }
    Integer& operator ++ (int) {
      val_++;
      return *this;
    }
  private :
    static Integer me_stat;
    size_t val_;
  };

  Integer Integer::me_stat (0);


  int main (int argc, char* argv []) {
    Integer i (7);
    std::cout << "i == ";
    i.write (std::cout);
    std::cout << std::endl;
    std::cout << "i next == ";
    i.write_next (std::cout);
    std::cout << std::endl;
    std::cout << "i again == ";
    i.write (std::cout);
    std::cout << std::endl;
  }

results :
  i == 7
  i next == 8
  i again == 7
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.

To post to this group, send email to std-pr...@isocpp.org.

Jake Arkinstall

unread,
Mar 6, 2018, 9:25:31 AM3/6/18
to std-pr...@isocpp.org
This isn't thread safe, so I'd throw a mutex in there. Also it is no different to just creating a new object when you call the method, except that you're keeping a temporary object in memory for the duration of the program. If your object is designed to be short-lived, e.g. it utilises RAII, then keeping it alive for longer than anticipated might cause confusion; the lifetime is determined not by the object itself, but by the next object to replace it (or by the program terminating).

I still think the optimal solution is usually going to be a view. This allows you to keep the underlying object completely unchanged and prevents you from having to copy the intended object at all. You can also chain these views, storing the underlying const reference each time, and successively adding the addition parameter.

class addition_view{
public:
    addition_view(const Integer& underlying, size_t to_add)
    :
        underlying(underlying),
        to_add(to_add)
    {}
    addition_view(const addition_view& other, size_t to_add)
    :
        underlying(other.underlying),
        to_add(other.to_add + to_add)
    {}
    void write(std::ostream& os) const{
        os << "The number is "
           << (underlying.value + to_add);
    }
protected:
    const Integer& underlying;
    size_t to_add;
};

You can then make addition_view a friend class of Integer and utilise it whenever you want a const method that "appears" to modify the value. The benefit for a single size_t is minimal, but when you're working with many values with fewer modifiers (e.g. your original example of 1000 size_ts with a single to_add that applies to all) it can really start to show memory improvements - after all, you'd only be creating a single size_t to_add, reading from the existing 1000 values, then freeing to_add.

To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.

To post to this group, send email to std-pr...@isocpp.org.

jsaintm...@gmail.com

unread,
Mar 6, 2018, 2:22:22 PM3/6/18
to ISO C++ Standard - Future Proposals
I don't care about Thread Safe Stuff. This is a sample example, to show "how it's working". Feel free to add them. Who is RAII ?

Jake Arkinstall

unread,
Mar 6, 2018, 2:38:44 PM3/6/18
to std-pr...@isocpp.org
On 6 Mar 2018 19:22, <jsaintm...@gmail.com> wrote:
I don't care about Thread Safe Stuff.

You want to submit a proposal to people who do. I recommend you start caring about thread safety.
Message has been deleted

jsaintm...@gmail.com

unread,
May 11, 2018, 11:46:08 AM5/11/18
to ISO C++ Standard - Future Proposals

okie : added throw and a mutex

  #include <mutex>

  class Integer {
  public :
    static std::mutex le_mutex;
    Integer (size_t s) throw (): val_ (s)  {}
    ~Integer () {}
    Integer (const Integer& i) throw () : val_ (i.val_)  {}
    Integer& operator = (const Integer& i) throw () {val_ = i.val_; return *this;}
    bool operator == (const Integer& i) throw () {return (val_ == i.val_?true:false);}
    bool operator != (const Integer& i) throw () {return !operator == (i);}

    void write (std::ostream& os) const throw () {
      os << val_;
    }
    void write_next (std::ostream& os) const throw () {
      le_mutex.lock ();

      me_stat = *this;
      me_stat++;
      me_stat.write (os);
      le_mutex.unlock ();
    }
    Integer& operator ++ (int) throw () {

      val_++;
      return *this;
    }
  private :
    static Integer me_stat;
    size_t val_;
  };

  std::mutex Integer::le_mutex;

jsaintm...@gmail.com

unread,
Jun 12, 2018, 6:08:57 AM6/12/18
to ISO C++ Standard - Future Proposals
As far as I know, you cant find an iterator in a map with hint method, but it is possible to insert a pair with a hint (faster).
With pseudo consness, you may insert with hint a pair in a map to find an iterator, return that iterator if it was inserted or an iterator to the end of the map. Map find (ihint) pseudocont :

std::map<size_t, size_t>::iterator find (std::map<size_t, size_t>& m, std::map<size_t, size_t>::iterator& ihint, size_t s)  /*pseudo const*/ {
  std::pair<size_t, size_t> p (s, (size_t) -1);
  std::map<size_t, size_t>::iterator i (m.begin ());
  i = m.insert (ihint, p);
  if (i->second == (size_t) -1) {
    m.erase (i);
    i = m.end ();
  }
  return i;

}

int main (int argc, char* argv []) {
  std::pair<size_t, size_t> tmp [5] = {
    std::pair<size_t, size_t> (2, 5),
    std::pair<size_t, size_t> (3, 4),
    std::pair<size_t, size_t> (5, 2),
    std::pair<size_t, size_t> (8, 3),
    std::pair<size_t, size_t> (9, 7)
  };
  std::map<size_t, size_t> m;
  m.insert ((std::pair<size_t, size_t>*) tmp, (std::pair<size_t, size_t>*) &tmp [5]);
  std::cout << "m == " << std::endl;
  std::for_each (m.begin (), m.end (), [] (const std::pair<size_t, size_t>& p) {
    std::cout << p.first << "->" << p.second << std::endl;
  });
  {
    std::cout << "finding 5..." << std::endl;
    std::map<size_t, size_t>::iterator ihint (m.find (5)), i (m.begin ());
    i = find (m, ihint, 5);
    std::cout << "5 ";
    if (i == m.end ()) std::cout << "not in map" << std::endl;
    else std::cout << "i in map :" << i->first << "->" << i->second << std::endl;
  }
  {
    std::cout << "finding 6..." << std::endl;
    std::map<size_t, size_t>::iterator ihint (m.find (5)), i (m.begin ());
    i = find (m, ihint, 6);
    std::cout << "6 ";
    if (i == m.end ()) std::cout << "not in map" << std::endl;
    else std::cout << "i in map :" << i->first << "->" << i->second << std::endl;
  }

  return 0;
}

and the results :

m ==
2->5
3->4
5->2
8->3
9->7
finding 5...
5 i in map :5->2
finding 6...
6 not in map

Reply all
Reply to author
Forward
0 new messages