Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

The unintended consequences of the 'auto' keyword

170 views
Skip to first unread message

Juha Nieminen

unread,
Apr 14, 2020, 6:03:40 AM4/14/20
to
The (new meaning of the) 'auto' keyword can be extremely useful in many
situations (and in fact the language uses it to its advantage to abstract
away types that the programmer doesn't need to care about, most prominently
the exact type of lamdba functions).

However, I have noticed that it has had a rather unintended consequence.

Namely, I have noticed that beginner and mid-tier C++ programmers (and
probably even some experienced programmers) are shoving it *absolutely
everywhere.* For reasons that I cannot really understand. Almost anywhere
where 'auto' could be used, they will use it.

No longer will you see code like

int i = 5;

Instead, it's always

auto i = 5;

And what happens if the codes wants it to be something other than an int?
I have seen abominations like

auto i = (short)5;

for reasons known only to the programmer himself. And probably not even to
himself.

Also things like:

auto name = std::string(str);

instead of, you know,

std::string name(str);

Recently I had to answer a beginner programmer's question why this didn't
work properly:

for(auto i = 0; i < vec.size(); ++i)

My response? "Don't use auto."

My followup response? "Don't. Use. auto."

Many a bug is introduced into programs due to overuse of auto. While there
are some genuine situations where it's actually desirable to write something
like

auto result = someFunctionSomewhere();

in general you really want to see the actual type you are expecting that
function to return. (There are situations where you don't and shouldn't
care about that type, but those tend to be rare.)

Personally, about the only situation where I allow myself to use 'auto'
merely to save writing is with iterators, like:

std::map<std::string, std::string> myMap;
auto iter = myMap.begin();

In most situations this is quite readable, understandable and not very
error-prone.

Paavo Helde

unread,
Apr 14, 2020, 6:32:14 AM4/14/20
to
14.04.2020 13:03 Juha Nieminen kirjutas:
> The (new meaning of the) 'auto' keyword can be extremely useful in many
> situations (and in fact the language uses it to its advantage to abstract
> away types that the programmer doesn't need to care about, most prominently
> the exact type of lamdba functions).
>
> However, I have noticed that it has had a rather unintended consequence.
>
> Namely, I have noticed that beginner and mid-tier C++ programmers (and
> probably even some experienced programmers) are shoving it *absolutely
> everywhere.* For reasons that I cannot really understand. Almost anywhere
> where 'auto' could be used, they will use it.

Looks like the code review step in your organization is not working
properly.

Chris Vine

unread,
Apr 14, 2020, 8:10:37 AM4/14/20
to
Herb Sutter is partly responsible for this with his "Almost Always
Auto", which I think was a mistake. (He also perpetrated a more serious
piece of disinformation with his "const means thread-safe": So
std::string::length() is thread-safe is it? I think not - try calling
it when another thread is appending to the string.)

Öö Tiib

unread,
Apr 14, 2020, 8:34:06 AM4/14/20
to
After 2002 or so I have gradually stopped listening to Herb Sutter since
he has become evangelist of C++/CLI and other as monstrous concepts.

Juha Nieminen

unread,
Apr 14, 2020, 8:35:34 AM4/14/20
to
Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote:
> (He also perpetrated a more serious
> piece of disinformation with his "const means thread-safe": So
> std::string::length() is thread-safe is it? I think not - try calling
> it when another thread is appending to the string.)

Did he say "thread-safe" or "reentrant"? Because there's a difference
between the two, and he probably meant the latter (even if he said
the former). And not as in "const guarantees reentrancy" but
"in well-designed code const *should* guarantee reentrancy".

(Reentrancy simply means that two threads can call the same funtion
simultaneously without any misbehavior happening. In other words,
the function is "thread-safe" with itself, ie. in the context of
being called from multiple threads at the same time.)

This means effectively that const functions shouldn't have
side-effects. Even if the function doesn't modify *this, it should
still have no side-effects (such as calling std::cout, etc).

Chris Vine

unread,
Apr 14, 2020, 12:33:05 PM4/14/20
to
He deduced that "const means thread safe" in the context of const member
functions, in a talk he gave at 2012 CPPCon. He observed that the
requirements of §17.6.5.9 of C++11 apply to any standard library
function, and in particular that §17.6.5.9/3 states that "A C++
standard library function shall not directly or indirectly modify
objects accessible by threads other than the current thread unless the
objects are accessed directly or indirectly via the function’s
non-const arguments, including this". (He also correctly observed that
this rule does not apply to correctly synchronised mutable data
members, which can be modified by a const this pointer - in other
words by const methods.) But he then confused himself (or at least gave
a confusingly idiosyncratic meaning to the expression "thread-safe") by
deducing that const members functions were therefore "thread-safe".

The ordinary meaning these days of applying the label "thread-safe" to
a method of an object is (I suggest) that a thread can call the method
without creating a data race by virtue of something that another thread
is doing to the same object concurrently: or in other words, that the
user does not have to provide his/her own synchronisation to prevent
undefined behaviour.

std::string::length() is re-entrant. It is also const. But it is not
thread-safe according to the normal meaning of the expression. You
have undefined behaviour if you call std::string::length() concurrently
with, amongst other things, another thread appending to the string.

What the extract from the standard I have mentioned does guarantee is
that different threads can concurrently call const methods of a class
in the standard library without external synchronisation provided that
(i) that no other thread calls a non-const method of the object
concerned concurrently, and (ii) that none of the const methods
modifies unsynchronised static or global data.

On a minor quibble about what you say, it may be true that generally
const methods 'shouldn't have side effects' (in the sense of 'I
wouldn't do that'), but that is not a requirement of the standard.
Const methods cannot modify non-mutable non-static data members
according to the standard. However they can modify static member data,
and any data in namespace scope. Or put another way, 'const' doesn't
mean 'pure'.

Daniel P

unread,
Apr 14, 2020, 1:19:05 PM4/14/20
to
On Tuesday, April 14, 2020 at 12:33:05 PM UTC-4, Chris Vine wrote:
>
> What the extract from the standard I have mentioned does guarantee is
> that different threads can concurrently call const methods of a class
> in the standard library without external synchronisation provided that
> (i) that no other thread calls a non-const method of the object
> concerned concurrently, and (ii) that none of the const methods
> modifies unsynchronised static or global data.

Also note that the std::function operator() const member function will do
whatever your containing code does.
>
> Const methods cannot modify non-mutable non-static data members
> according to the standard. However they can modify static member data,
> and any data in namespace scope.

They can modify the contents of a derefernced pointer data member,

class A
{
public:
int* p;

A()
{
p = new int [2];
}

~A()
{
delete p;
}

void foo() const
{
p[0] = 1;
}
};

> Or put another way, 'const' doesn't mean 'pure'.

Indeed. Generally, C++ const means no more than "logical const", that the
"logical" state of the object remains the same. Not something the compiler
can use to optimize or diagnose. And of limited value for reasoning about
code.

Daniel

Öö Tiib

unread,
Apr 14, 2020, 1:45:52 PM4/14/20
to
On Tuesday, 14 April 2020 20:19:05 UTC+3, Daniel P wrote:
>
> > Or put another way, 'const' doesn't mean 'pure'.
>

The thing that in C++ is very close to meaning "pure" about function
is "constexpr". In fact constexpr function is related to constant
expressions only to extent what one would expect from pure functions.

Keith Thompson

unread,
Apr 14, 2020, 2:04:46 PM4/14/20
to
Chris Vine <chris@cvine--nospam--.freeserve.co.uk> writes:
[...]
> Herb Sutter is partly responsible for this with his "Almost Always
> Auto", which I think was a mistake. (He also perpetrated a more serious
> piece of disinformation with his "const means thread-safe": So
> std::string::length() is thread-safe is it? I think not - try calling
> it when another thread is appending to the string.)

Another thread can't append to the string if the string is defined as
"const". (I haven't read Herb Sutter's "const means thread-safe"
discussion.)

--
Keith Thompson (The_Other_Keith) Keith.S.T...@gmail.com
Working, but not speaking, for Philips Healthcare
void Void(void) { Void(); } /* The recursive call of the void */

Chris Vine

unread,
Apr 14, 2020, 2:34:22 PM4/14/20
to
On Tue, 14 Apr 2020 11:04:33 -0700
Keith Thompson <Keith.S.T...@gmail.com> wrote:
> Chris Vine <chris@cvine--nospam--.freeserve.co.uk> writes:
> [...]
> > Herb Sutter is partly responsible for this with his "Almost Always
> > Auto", which I think was a mistake. (He also perpetrated a more serious
> > piece of disinformation with his "const means thread-safe": So
> > std::string::length() is thread-safe is it? I think not - try calling
> > it when another thread is appending to the string.)
>
> Another thread can't append to the string if the string is defined as
> "const". (I haven't read Herb Sutter's "const means thread-safe"
> discussion.)

Clearly (on both points). The discussion was about whether const
methods are thread safe.

But the point also remains about objects held by reference to const.
The fact that thread 1 cannot modify an object because it only holds
the object by reference to const (say by receiving it as a reference
to const function argument) does not mean that thread 2 cannot access
it in some other way and thus modify it. You have to look at the code
to determine whether there is the potential for a data race, not at
whether the method concerned is qualified as const.

Keith Thompson

unread,
Apr 14, 2020, 3:33:10 PM4/14/20
to
Chris Vine <chris@cvine--nospam--.freeserve.co.uk> writes:
> On Tue, 14 Apr 2020 11:04:33 -0700
> Keith Thompson <Keith.S.T...@gmail.com> wrote:
>> Chris Vine <chris@cvine--nospam--.freeserve.co.uk> writes:
>> [...]
>> > Herb Sutter is partly responsible for this with his "Almost Always
>> > Auto", which I think was a mistake. (He also perpetrated a more serious
>> > piece of disinformation with his "const means thread-safe": So
>> > std::string::length() is thread-safe is it? I think not - try calling
>> > it when another thread is appending to the string.)
>>
>> Another thread can't append to the string if the string is defined as
>> "const". (I haven't read Herb Sutter's "const means thread-safe"
>> discussion.)
>
> Clearly (on both points). The discussion was about whether const
> methods are thread safe.

OK.

> But the point also remains about objects held by reference to const.
> The fact that thread 1 cannot modify an object because it only holds
> the object by reference to const (say by receiving it as a reference
> to const function argument) does not mean that thread 2 cannot access
> it in some other way and thus modify it. You have to look at the code
> to determine whether there is the potential for a data race, not at
> whether the method concerned is qualified as const.

Sure. Which is why I specifically said that the string (by which I
meant an object of type std::string) is *defined* as const, not that
something sees it as const.

Admittedly my point was a bit trivial.

Chris Vine

unread,
Apr 14, 2020, 6:52:12 PM4/14/20
to
On Tue, 14 Apr 2020 12:32:56 -0700
There is an interesting tangential point. How often in a C++ program
do people create const immutable class type objects for run time (as
opposed to, say, constexpr scalars for compile-time computation)? I
can't say my code does it at all often, if at all. In the case of
strings, if I want a literal string I tend to use a C string literal.

C++ is an imperative language, not a functional language. In C++ you
don't loop by iterative recursion and the passing of immutable
arguments with tail call elimination, you loop with mutable local loop
variables. The C++ OOP paradigm usually assumes that at some point
some method is going to mutate a datum at some point in the program -
that is the whole point of encapsulating data with the methods which
will operate on the data.

It is commonplace to pass non-const non-trivial objects to a function
by reference to const on the understanding that they will not be
mutated by that particular function, but I think it is quite rare in
C++ to create such objects as immutable and const at the outset.

michae...@gmail.com

unread,
Apr 14, 2020, 8:41:13 PM4/14/20
to
Great observations here. There's a time and a place for everything, and if you need to be explicit about the underlying type, do not use auto. If you do not want to depend on inferred type of a thing, do not use auto. For instance if you had to revise a vector or other collection from const to non-const, along those lines. Do not assume that the iterator would be the same. And so on.

Cholo Lennon

unread,
Apr 14, 2020, 9:25:49 PM4/14/20
to
On 4/14/20 7:03 AM, Juha Nieminen wrote:
> The (new meaning of the) 'auto' keyword can be extremely useful in many
> situations (and in fact the language uses it to its advantage to abstract
> away types that the programmer doesn't need to care about, most prominently
> the exact type of lamdba functions).

Totally agree.

>
> However, I have noticed that it has had a rather unintended consequence.
>
> Namely, I have noticed that beginner and mid-tier C++ programmers (and
> probably even some experienced programmers) are shoving it *absolutely
> everywhere.* For reasons that I cannot really understand. Almost anywhere
> where 'auto' could be used, they will use it.
>

Well, C++ developers are going in the opposite direction of Python ones.
While Python has added data "type hints" in order to solve the mess that
Python code is, we started using auto in every place as you noted, which
complicates reading and tools, just to save some keystrokes. I know that
it's no fair to compare a static language with a dynamic one, but maybe
we should learn this lesson from Python.


> No longer will you see code like
>
> int i = 5;
>
> Instead, it's always
>
> auto i = 5;
>
> And what happens if the codes wants it to be something other than an int?
> I have seen abominations like
>
> auto i = (short)5;
>

OMG! :-O

> for reasons known only to the programmer himself. And probably not even to
> himself.
>
> Also things like:
>
> auto name = std::string(str);
>
> instead of, you know,
>
> std::string name(str);
>
> Recently I had to answer a beginner programmer's question why this didn't
> work properly:
>
> for(auto i = 0; i < vec.size(); ++i)
>
> My response? "Don't use auto."
>
> My followup response? "Don't. Use. auto."
>
> Many a bug is introduced into programs due to overuse of auto. While there
> are some genuine situations where it's actually desirable to write something
> like
>
> auto result = someFunctionSomewhere();
>
> in general you really want to see the actual type you are expecting that
> function to return. (There are situations where you don't and shouldn't
> care about that type, but those tend to be rare.)
>

Yes, exactly, this is one of my concerns about auto in C++ (and in
Python without type hints), I need to know the return type in order to
undestand the code (or to operate with it). Sometimes is very easy to
follow the code, but in other cases is a nightmare.


> Personally, about the only situation where I allow myself to use 'auto'
> merely to save writing is with iterators, like:
>
> std::map<std::string, std::string> myMap;
> auto iter = myMap.begin();
>
> In most situations this is quite readable, understandable and not very
> error-prone.
>

I have the same rule

--
Cholo Lennon
Bs.As.
ARG

Juha Nieminen

unread,
Apr 15, 2020, 2:22:09 AM4/15/20
to
Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote:
> On a minor quibble about what you say, it may be true that generally
> const methods 'shouldn't have side effects' (in the sense of 'I
> wouldn't do that'), but that is not a requirement of the standard.

That's why I said (trying to paraphrase what I believe Herb Shutter meant)
"in well-designed code".

Generally the standard doesn't take a stance on code quality.

Jorgen Grahn

unread,
Apr 15, 2020, 3:34:39 AM4/15/20
to
On Tue, 2020-04-14, Daniel P wrote:
...
> Indeed. Generally, C++ const means no more than "logical const", that the
> "logical" state of the object remains the same. Not something the compiler
> can use to optimize or diagnose. And of limited value for reasoning about
> code.

That value is limited (like most values) but IMO great. It's one of
the best things about C and C++.

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

Mr Flibble

unread,
Apr 15, 2020, 7:13:46 AM4/15/20
to
auto is great; use almost everywhere! Saying that a construct such as 42i16 or 42u16 would be nice for integer literals; can we have that with the user defined literals?

/Flibble

--
"Snakes didn't evolve, instead talking snakes with legs changed into snakes." - Rick C. Hodgin

“You won’t burn in hell. But be nice anyway.” – Ricky Gervais

“I see Atheists are fighting and killing each other again, over who doesn’t believe in any God the most. Oh, no..wait.. that never happens.” – Ricky Gervais

"Suppose it's all true, and you walk up to the pearly gates, and are confronted by God," Byrne asked on his show The Meaning of Life. "What will Stephen Fry say to him, her, or it?"
"I'd say, bone cancer in children? What's that about?" Fry replied.
"How dare you? How dare you create a world to which there is such misery that is not our fault. It's not right, it's utterly, utterly evil."
"Why should I respect a capricious, mean-minded, stupid God who creates a world that is so full of injustice and pain. That's what I would say."

Ned Latham

unread,
Apr 15, 2020, 7:35:23 AM4/15/20
to
Mr Flibble wrote:

----snip----

> auto is great; use almost everywhere! Saying that a construct such as
> 42i16 or 42u16 would be nice for integer literals; can we have that
> with the user defined literals?

They have to be interpreted for computation. So you might as well
define them as strings and gain the compiler checking.

David Brown

unread,
Apr 15, 2020, 8:27:45 AM4/15/20
to
On 15/04/2020 13:13, Mr Flibble wrote:

> auto is great; use almost everywhere! Saying that a construct such as
> 42i16 or 42u16 would be nice for integer literals; can we have that with
> the user defined literals?
>

Sure:

#include <stdint.h>

int16_t operator"" i16(unsigned long long x) {
return x;
}

uint16_t operator"" u16(unsigned long long x) {
return x;
}

I guess it would be easy to have these standardised, if someone thinks
they would be useful enough to justify the effort in all the
documentation and bureaucracy involved (which would far outweigh the
coding effort).

jacobnavia

unread,
Apr 15, 2020, 8:59:45 AM4/15/20
to
Le 15/04/2020 à 14:27, David Brown a écrit :
> On 15/04/2020 13:13, Mr Flibble wrote:
>
>> auto is great; use almost everywhere! Saying that a construct such as
>> 42i16 or 42u16 would be nice for integer literals; can we have that
>> with the user defined literals?
>>
>
> Sure:
>
> #include <stdint.h>
>
> int16_t operator"" i16(unsigned long long x) {
>     return x;
> }
>

int16_t operator"" i16(long long x) {
if (x < 32768 && x >= -32767) ) return x;
throw("overflow");
}

Note the long long and NOT unsigned long long since the result is signed


> uint16_t operator"" u16(unsigned long long x) {
>     return x;
> }
>

uint16_t operator"" i16(long long x) {
if (x < 65536 ) ) return x;
throw("overflow");
}

Isn't error checking an essential part of writing code?
Or did I miss something? (My C++ is not very good)

David Brown

unread,
Apr 15, 2020, 11:03:29 AM4/15/20
to
On 15/04/2020 14:59, jacobnavia wrote:
> Le 15/04/2020 à 14:27, David Brown a écrit :
>> On 15/04/2020 13:13, Mr Flibble wrote:
>>
>>> auto is great; use almost everywhere! Saying that a construct such as
>>> 42i16 or 42u16 would be nice for integer literals; can we have that
>>> with the user defined literals?
>>>
>>
>> Sure:
>>
>> #include <stdint.h>
>>
>> int16_t operator"" i16(unsigned long long x) {
>>      return x;
>> }
>>
>
> int16_t operator"" i16(long long x) {
>      if (x < 32768 && x >= -32767) ) return x;
>      throw("overflow");
> }
>
> Note the long long and NOT unsigned long long since the result is signed

No.

You can't have "long long" as the parameter. (I tested my code - did you?)

Although it might seem like the minus sign is part of the integer
literal, it is not. "-1234" is a unary minus operator and an integer
literal 1234. "-1234i16" is a unary minus operator applied to the
result of operator"" i16(1234).

Thus the parameter must always be unsigned long long.

Having a "throw" looks nice, but has the disadvantage of being a
run-time check, when you want a compile-time check.

With C++20 "consteval", it's easy - a user-defined literal can be
declared "consteval", and then the throw turns into a compile-time error.

Getting a result that will work as desired for -32768_i16 through to
32767_i16, but give compile-term errors outside that range, is not easy.

>
>
>> uint16_t operator"" u16(unsigned long long x) {
>>      return x;
>> }
>>
>
> uint16_t operator"" i16(long long x) {
>      if (x < 65536 ) ) return x;
>      throw("overflow");
> }
>
> Isn't error checking an essential part of writing code?
> Or did I miss something? (My C++ is not very good)
>

The ideal is compile-time errors for things like this (since we are
talking about literals, they are known at compile time).

The best I could come up with is by using C++ "consteval" and a proxy class:

#include <stdint.h>

class int16_t_proxy {
private:
int32_t x_;
constexpr int16_t_proxy(int32_t x) : x_(x) {};
public:
friend consteval int16_t_proxy operator"" _i16(unsigned long long x);
consteval int16_t_proxy operator-() {
return -x_;
}
consteval operator int16_t() {
if (x_ > 32767) throw "overflow";
return x_;
};
};

consteval int16_t_proxy operator"" _i16(unsigned long long x) {
if (x > 32768) throw "overflow";
return x;
}

int16_t xx1() {
return 32767_i16;
}

int16_t xx2() {
return -32768_i16;
}


Unfortunately, compilers don't seem to be ready for this. Clang trunk
uses a run-time exception for "32768_i16", though it correctly gives a
compile-time error for bigger values. I.e., it does not allow a throw
in the consteval operator""_i16(), but it /does/ allow it in the
consteval operator int16_t(). I believe clang is wrong here.

And while gcc trunk /does/ give a compile time error on 32768_i16, it
gives an internal compiler error for -32768_i16 (and any other use of
the operator-()).

(This is testing with <https://godbolt.org>.)


If my code is wrong, or my interpretation of clang and gcc is wrong, or
there are improvements to make in the code (other than obvious things
like naming) or other approaches, I would be glad to hear from others.

Alf P. Steinbach

unread,
Apr 15, 2020, 2:58:18 PM4/15/20
to
On 15.04.2020 17:03, David Brown wrote:
> On 15/04/2020 14:59, jacobnavia wrote:
>> Le 15/04/2020 à 14:27, David Brown a écrit :
>>> On 15/04/2020 13:13, Mr Flibble wrote:
>>>
>>>> auto is great; use almost everywhere! Saying that a construct such
>>>> as 42i16 or 42u16 would be nice for integer literals; can we have
>>>> that with the user defined literals?
>>>>
>>>
>>> Sure:
>>>
>>> #include <stdint.h>
>>>
>>> int16_t operator"" i16(unsigned long long x) {
>>>      return x;
>>> }
>>>
>>
>> int16_t operator"" i16(long long x) {
>>       if (x < 32768 && x >= -32767) ) return x;
>>       throw("overflow");
>> }
>>
>> Note the long long and NOT unsigned long long since the result is signed
>
> No.
>
> You can't have "long long" as the parameter.  (I tested my code - did you?)
>
> Although it might seem like the minus sign is part of the integer
> literal, it is not.  "-1234" is a unary minus operator and an integer
> literal 1234.  "-1234i16" is a unary minus operator applied to the
> result of operator"" i16(1234).


Oh dear, that seems to imply that the usual arithmetic conversions will
kick in, making -42i16 produce an `int`?

Gah!


> [snip]

- Alf

James Kuyper

unread,
Apr 15, 2020, 3:55:10 PM4/15/20
to
On 4/15/20 2:58 PM, Alf P. Steinbach wrote:
> On 15.04.2020 17:03, David Brown wrote:
>> On 15/04/2020 14:59, jacobnavia wrote:
>>> Le 15/04/2020 à 14:27, David Brown a écrit :
...
>>> int16_t operator"" i16(long long x) {
>>>       if (x < 32768 && x >= -32767) ) return x;
>>>       throw("overflow");
>>> }
>>>
>>> Note the long long and NOT unsigned long long since the result is signed
>>
>> No.
>>
>> You can't have "long long" as the parameter.  (I tested my code - did you?)
>>
>> Although it might seem like the minus sign is part of the integer
>> literal, it is not.  "-1234" is a unary minus operator and an integer
>> literal 1234.  "-1234i16" is a unary minus operator applied to the
>> result of operator"" i16(1234).
>
>
> Oh dear, that seems to imply that the usual arithmetic conversions will
> kick in, making -42i16 produce an `int`?

The usual arithmetic conversions only apply to binary operators, not
unary '-'. You may be thinking of the integral promotions. It will be
promoted to int if the integer conversion rank of int16_t is less than
that of int and "int can represent all the values of" int16_t (7.6p1).

Do you see that as problematic?

Alf P. Steinbach

unread,
Apr 15, 2020, 4:17:22 PM4/15/20
to
Yes, it is.

- Alf

Daniel P

unread,
Apr 15, 2020, 4:51:05 PM4/15/20
to
On Wednesday, April 15, 2020 at 7:13:46 AM UTC-4, Mr Flibble wrote:
>
> auto is great; use almost everywhere!

Just not here

Matrix2d mat;
mat << 1, 2,
3, 4;
Vector2d u(-1,1), v(2,0);
std::cout << "Here is mat*mat:\n" << mat*mat << std::endl;
std::cout << "Here is mat*u:\n" << mat*u << std::endl;
std::cout << "Here is u^T*mat:\n" << u.transpose()*mat << std::endl;
std::cout << "Here is u^T*v:\n" << u.transpose()*v << std::endl;
std::cout << "Here is u*v^T:\n" << u*v.transpose() << std::endl;
std::cout << "Let's multiply mat by itself" << std::endl;
mat = mat*mat;

Daniel P

unread,
Apr 15, 2020, 4:59:15 PM4/15/20
to
On Wednesday, April 15, 2020 at 7:13:46 AM UTC-4, Mr Flibble wrote:

> auto is great; use almost everywhere!

Except:

Eigen::Matrix2d mat;
mat << 1, 2,
3, 4;
Eigen::Matrix2d mat = mat*mat; // NOT HERE

and

std::vector<bool> v{true, false};
bool val = v[1]; // NOT HERE

and

xt::xarray<double> arr1
{{1.0, 2.0, 3.0},
{2.0, 5.0, 7.0},
{2.0, 5.0, 7.0}};

xt::xarray<double> arr2 = arr1 + arr2; // NOT HERE

etc.

Daniel


James Kuyper

unread,
Apr 15, 2020, 5:04:01 PM4/15/20
to
Could you explain that a little more? Specifically, could you give an
example of how it could cause problems?

I've worked with C/C++ for 40 years now. In both of those languages, the
integral promotions (in C, they're called integer promotions) apply to
any integer type with an integer conversion rank less than that of int
for which all of it's values can be represented as an 'int'. That is
guaranteed to be true of signed char, and short. On most common systems
today, they also apply to char, unsigned char, unsigned short,
[u]int8_t, and [u]int16_t, [u]int_least8_t, [u]int_least16_t,
[u]int_fast8_t, and [u]intfast16_t. I've used systems where 'int' is a
64-bit type, on which [u]int32_t, [u]int_least32_t and [u]int_fast32_t
are also subject to promotion. And while those promotions do
occasionally cause problems, they're very far from being the biggest,
most common, or most difficult to deal with problems I run into.

One key reason why they tend to be unproblematic is that the integral
promotions, as a matter of deliberate design, never change the value of
an expression, only it's type.

Louis Krupp

unread,
Apr 15, 2020, 6:46:26 PM4/15/20
to
On 4/14/2020 6:35 AM, Juha Nieminen wrote:
> Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote:
>> (He also perpetrated a more serious
>> piece of disinformation with his "const means thread-safe": So
>> std::string::length() is thread-safe is it? I think not - try calling
>> it when another thread is appending to the string.)
>
> Did he say "thread-safe" or "reentrant"? Because there's a difference
> between the two, and he probably meant the latter (even if he said
> the former). And not as in "const guarantees reentrancy" but
> "in well-designed code const *should* guarantee reentrancy".
>
> (Reentrancy simply means that two threads can call the same funtion
> simultaneously without any misbehavior happening. In other words,
> the function is "thread-safe" with itself, ie. in the context of
> being called from multiple threads at the same time.)

As I understand it, reentrancy is an issue even when there's only one
thread. Some links:

https://www.geeksforgeeks.org/reentrant-function/

https://deadbeef.me/2017/09/reentrant-threadsafe

An example of a function that is thread-safe but not necessarily
reentrant would be one that writes and reads a thread-local variable
while calling itself.

Louis

Alf P. Steinbach

unread,
Apr 15, 2020, 9:44:31 PM4/15/20
to
The purpose of an `operator""i16` is to control the type.

So with all those words you're ignoring the main aspect.

Since you ask for an example, `foo( -42i16 )` can do the entirely wrong
(unintended) thing, for example if foo copies a value into a memory area
representing a file header, and advances a write position.

It could even be that foo is so badly designed that it does slightly
different but conceptually similar things for `int` and `int16_t`. I
recently had experience with the FLTK GUI library, used in Bjarne's
intro book, and it does slightly different things for `double` versus
`int` coordinates in graphics, e.g. whether it involves transformation.
When one deals with libraries like that controlling the type is key.

There is another important aspect, that of the language being
predictable, no big surprises, and not having meaningless constructs.
Getting type `int` out of `int16_t(-42)` renders it meaningless. So
that's a design level bug in the scheme for user defined literals.

- Alf

Alf P. Steinbach

unread,
Apr 15, 2020, 9:49:41 PM4/15/20
to
Sorry, paste error, should be "int` out of `-42i16`".


James Kuyper

unread,
Apr 15, 2020, 10:40:21 PM4/15/20
to
On Wednesday, April 15, 2020 at 9:44:31 PM UTC-4, Alf P. Steinbach wrote:
> On 15.04.2020 23:03, James Kuyper wrote:
> > On 4/15/20 4:17 PM, Alf P. Steinbach wrote:
> >> On 15.04.2020 21:54, James Kuyper wrote:
> >>> On 4/15/20 2:58 PM, Alf P. Steinbach wrote:
> >>>> On 15.04.2020 17:03, David Brown wrote:
...
Yes, but that's nothing new. Given

constexpr int16_t negfortytwo = -42;

negfortytwo has a type that is very well-defined, but despite
that fact, you'll have exactly the same problems with negfortytwo
that you would with -42i16. It might be annoying that the ability
to specify user-defined literals doesn't solve this problem, but
its not a disaster, it's something that every experienced C or
C++ programmer has been dealing with for as long as they've been
using those languages.

> It could even be that foo is so badly designed that it does slightly
> different but conceptually similar things for `int` and `int16_t`. I
> recently had experience with the FLTK GUI library, used in Bjarne's
> intro book, and it does slightly different things for `double` versus
> `int` coordinates in graphics, e.g. whether it involves transformation.
> When one deals with libraries like that controlling the type is key.

The integral promotions never result in a floating point type.
The usual arithmetic conversions do so only if one of the two
types involved is a floating point type. It strikes me as quite
reasonable that int coordinates and double coordinates do things
somewhat differently - there's things that you can't do with int
coordinates because they have a much larger granularity.

> There is another important aspect, that of the language being
> predictable, no big surprises, and not having meaningless constructs.
> Getting type `int` out of `int16_t(-42)` renders it meaningless. So
> that's a design level bug in the scheme for user defined literals.

It would be more of a surprise, at least to experienced
programmers, if the integral promotions don't occur just because
user defined literals are involved. It would be slightly less
confusing if the integer promotions were simply removed
completely, rather than setting up a special exemption from them
for user define integer literals.

Alf P. Steinbach

unread,
Apr 15, 2020, 11:36:32 PM4/15/20
to
No. You fail to understand that if `foo` depends on the type of the
argument, then it depends on the type of the argument. The dependency I
sketched was how many bytes it would write and advance a write position,
but there are infinitely many other reasonable such dependencies in C++.


> It might be annoying that the ability
> to specify user-defined literals doesn't solve this problem, but
> its not a disaster, it's something that every experienced C or
> C++ programmer has been dealing with for as long as they've been
> using those languages.

No, they haven't. It gives the impression that you have very little
experience. I know that to be probably false, so it's baffling.


>> It could even be that foo is so badly designed that it does slightly
>> different but conceptually similar things for `int` and `int16_t`. I
>> recently had experience with the FLTK GUI library, used in Bjarne's
>> intro book, and it does slightly different things for `double` versus
>> `int` coordinates in graphics, e.g. whether it involves transformation.
>> When one deals with libraries like that controlling the type is key.
>
> The integral promotions never result in a floating point type.

That's an irrelevant observation.


> The usual arithmetic conversions do so only if one of the two
> types involved is a floating point type. It strikes me as quite
> reasonable that int coordinates and double coordinates do things
> somewhat differently - there's things that you can't do with int
> coordinates because they have a much larger granularity.

I find it unreasonable. To me it's just an idiotic design. The rationale
of overloads is that they do the conceptually the same. When that isn't
the case, a decent design uses different names. And namespaces.


>> There is another important aspect, that of the language being
>> predictable, no big surprises, and not having meaningless constructs.
>> Getting type `int` out of `int16_t(-42)` renders it meaningless. So
>> that's a design level bug in the scheme for user defined literals.
>
> It would be more of a surprise, at least to experienced
> programmers, if the integral promotions don't occur just because
> user defined literals are involved. It would be slightly less
> confusing if the integer promotions were simply removed
> completely, rather than setting up a special exemption from them
> for user define integer literals.

Rather than special-casing the existing rules, as you seem to imagine, a
better design of user-defined literals would have removed the dependency
on a language feature (the sign operators) that renders many expressions
meaningless.


- Alf

David Brown

unread,
Apr 16, 2020, 4:03:02 AM4/16/20
to
No, it's different.

Let me show this with a program:


#include <iostream>
#include <stdint.h>

using namespace std;

void foo(int16_t x) { cout << "int16_t " << x << "\n"; }
void foo(int32_t x) { cout << "int32_t " << x << "\n"; }
void foo(int64_t x) { cout << "int64_t " << x << "\n"; }

int16_t operator"" _i16(unsigned long long x) {
return x;
}

class Int16_t {
private :
int16_t x_;
public :
constexpr Int16_t(int16_t x) : x_(x) {};
constexpr Int16_t operator-() { return -x_; }
constexpr operator int16_t() { return x_; }
};

Int16_t operator"" _I16(unsigned long long x) {
return x;
}

constexpr int16_t negfortytwo_a = -42;
const int16_t negfortytwo_b = -42;

int main() {
foo(123);
foo((int16_t) 123);
foo(-(int16_t) 123);
foo((int16_t) -123);
foo(42_i16);
foo(-42_i16);
foo(43_I16);
foo(-43_I16);
foo(negfortytwo_a);
foo(negfortytwo_b);
}

The output of this is:

int32_t 123
int16_t 123
int32_t -123
int16_t -123
int16_t 42
int32_t -42
int16_t 43
int16_t -43
int16_t -42
int16_t -42


None of this is a surprise when you know the integer promotion rules,
and when you understand that integer literals are always non-negative -
the minus sign is not part of the literal.

But having the minus sign not be part of the literal is
counter-intuitive, and goes against common mathematics. The number -42
is a negative number - it is not something we construct by taking a
positive number and negating it (though in mathematics we know the
result is the same).

Why would someone want an "_i16" user defined literal at all? Surely it
is to construct something specifically known to by of type int16_t, as
the value is not changed. And since it is going to be promoted in any
arithmetic context, you are going to want it in a context where type is
critical, such as for function overloading.

It might not be a /surprise/ that -42_i16 is an int, but it is certainly
going to be annoying and useless.

The answer here - as usual in C++ - is if you don't like the current
behaviour of a type, make a class that you /do/ like. It is possible to
make your own classes that don't promote - the Int16_t above is a start.


I can think of a couple more situations where integer promotion can
cause trouble, which could be solved by non-promoting types:

uint16_t a = 60000;
uint16_t b = a * a;

On a 16-bit int machine, "a * a" is done as unsigned arithmetic, and
gives you 41984 as the result. On a 32-bit int machine, the operands
are promoted to "int", you are calculating 60000 * 60000 in signed
integer arithmetic, and it overflows - potentially doing unpleasant
things. This is despite writing everything with unsigned types that
normally have defined wrapping.

And for a somewhat more niche case, on 8-bit systems (like the AVR - the
only 8-bit target supported by gcc, and (I think) llvm/clang) you have
16-bit ints. You usually want to do as much as possible using uint8_t
or int8_t for efficiency, but the language insists on bumping these up
to 16 bit before doing most arithmetic operations. Compilers can do a
reasonable job of eliminating some of the excess code, but sometimes
non-promoting user types work better.

On some 8-bit C compilers for embedded systems, integer promotion is not
applied. This results in more efficient code, and more confusion
because the code doesn't follow C rules.

James Kuyper

unread,
Apr 16, 2020, 11:56:06 AM4/16/20
to
On 4/15/20 11:36 PM, Alf P. Steinbach wrote:
> On 16.04.2020 04:40, James Kuyper wrote:
>> On Wednesday, April 15, 2020 at 9:44:31 PM UTC-4, Alf P. Steinbach wrote:
>>> On 15.04.2020 23:03, James Kuyper wrote:
...
>>>> Could you explain that a little more? Specifically, could you give an
>>>> example of how it could cause problems?

You've responded to me twice now, and still have not provided an
example. Source code demonstrating the problem you're worried about
would make your point much clearer than any amount of words could -
particular since it's become quite clear that we aren't communicating
with each other very well using words.

>>>> I've worked with C/C++ for 40 years now. In both of those languages, the
>>>> integral promotions (in C, they're called integer promotions) apply to
>>>> any integer type with an integer conversion rank less than that of int
>>>> for which all of it's values can be represented as an 'int'. That is
>>>> guaranteed to be true of signed char, and short. On most common systems
>>>> today, they also apply to char, unsigned char, unsigned short,
>>>> [u]int8_t, and [u]int16_t, [u]int_least8_t, [u]int_least16_t,
>>>> [u]int_fast8_t, and [u]intfast16_t. I've used systems where 'int' is a
>>>> 64-bit type, on which [u]int32_t, [u]int_least32_t and [u]int_fast32_t
>>>> are also subject to promotion. And while those promotions do
>>>> occasionally cause problems, they're very far from being the biggest,
>>>> most common, or most difficult to deal with problems I run into.
>>>>
>>>> One key reason why they tend to be unproblematic is that the integral
>>>> promotions, as a matter of deliberate design, never change the value of
>>>> an expression, only it's type.
>>>
>>> The purpose of an `operator""i16` is to control the type.
>>>
>>> So with all those words you're ignoring the main aspect.
>>>
>>> Since you ask for an example, `foo( -42i16 )` can do the entirely wrong
>>> (unintended) thing, for example if foo copies a value into a memory area
>>> representing a file header, and advances a write position.
>>
>> Yes, but that's nothing new. Given
>>
>> constexpr int16_t negfortytwo = -42;

Sorry - I muddied the waters by incorporating the '-' into that
definition. The parallels are clearer in the code below, which handles
that differently.

>> negfortytwo has a type that is very well-defined, but despite
>> that fact, you'll have exactly the same problems with negfortytwo
>> that you would with -42i16.
>
> No. You fail to understand that if `foo` depends on the type of the
> argument, then it depends on the type of the argument.

Note: according to n4659.pdf "Literal suffix identifiers (16.5.8) that
do not start with an underscore are reserved for future
standardization." (20.5.4.3.5). I found this out while testing the code
below, which wouldn't compile until I changed i16 to _i16.

#include <iostream>
#include <typeinfo>
int16_t operator"" _i16(unsigned long long x) {
return x;
}

constexpr int16_t fortytwo_16 = 42;

template<class T> void foo(T t) {
std::cout << typeid(t).name() << ":" << t << "\n";
}

int main(void)
{
foo(fortytwo_16);
foo(42_i16);
foo(-fortytwo_16);
foo(-42_i16);
return 0;
}

~/Programming/C++/testprog(61) ~/Programming/C++/testprog(60) g++
-std=c++1y -pedantic -Wall -Wpointer-arith -Wcast-align
-fno-enforce-eh-specs -ffor-scope -fno-gnu-keywords
-fno-nonansi-builtins -Wctor-dtor-privacy -Wnon-virtual-dtor
-Wold-style-cast -Woverloaded-virtual -Wsign-promo literal.cpp -o literal
~/Programming/C++/testprog(62) literal
s:42
s:42
i:-42
i:-42

Yes, foo does depend upon the type of the argument. And the integral
promotions triggered by the use of unary '-' do change the type. But
that's just as much of a problem with -42_i16 as it is with -fortytwo_16.


>> It might be annoying that the ability
>> to specify user-defined literals doesn't solve this problem, but
>> its not a disaster, it's something that every experienced C or
>> C++ programmer has been dealing with for as long as they've been
>> using those languages.
>
> No, they haven't. It gives the impression that you have very little
> experience. I know that to be probably false, so it's baffling.

I've been dealing with the problem shown by my code above for more than
30 years now, and it's not particularly difficult to do so. It predates
int16_t - the same problem comes up with "short" and "signed char". It
predates C++ - C has the same rule, though they are now called "integer
promotions" rather than "integral promotions". If that rule causes some
different kind of problem for user-defined literals, you've yet to
articulate what that problem is.

The clearest way to show the problems you're worrying about would be to
show me some code where -42_i16 causes a problem, but replacing it with
-fortytwo_16 does not.

>>> It could even be that foo is so badly designed that it does slightly
>>> different but conceptually similar things for `int` and `int16_t`. I
>>> recently had experience with the FLTK GUI library, used in Bjarne's
>>> intro book, and it does slightly different things for `double` versus
>>> `int` coordinates in graphics, e.g. whether it involves transformation.
>>> When one deals with libraries like that controlling the type is key.
>>
>> The integral promotions never result in a floating point type.
>
> That's an irrelevant observation.

You've complained about a problem supposedly caused by integral
promotions, and in the same context, a library that does different
things with integral and floating point arguments. If the fact that
integral promotions never result in a floating point type is irrelevant
to the second part of that complaint, then pray please explain the
connection that you do see between your previous complaint and this one.

...
>> It would be more of a surprise, at least to experienced
>> programmers, if the integral promotions don't occur just because
>> user defined literals are involved. It would be slightly less
>> confusing if the integer promotions were simply removed
>> completely, rather than setting up a special exemption from them
>> for user define integer literals.
>
> Rather than special-casing the existing rules, as you seem to imagine, a
> better design of user-defined literals would have removed the dependency
> on a language feature (the sign operators) that renders many expressions
> meaningless.

The use of user-defined literals is dependent on the integral promotions
entirely because the operator""() overloads are allowed to return
integral types that are smaller than int. The only way to remove that
dependency without special casing for user-defined literals would be to
prohibit them from returning integer types that are smaller than int.
And that would count as special-casing on the return type.

It would be far easier for me to have some idea what in the world you're
talking about if you would please identify specific clauses in the
standard that you think should be changed, and specify how they should
be changed. Please provide that actual new wording, not just a
description of the wording.

Alf P. Steinbach

unread,
Apr 16, 2020, 12:31:43 PM4/16/20
to
On 16.04.2020 17:55, James Kuyper wrote:
> On 4/15/20 11:36 PM, Alf P. Steinbach wrote:
>> On 16.04.2020 04:40, James Kuyper wrote:
>>> On Wednesday, April 15, 2020 at 9:44:31 PM UTC-4, Alf P. Steinbach wrote:
>>>> On 15.04.2020 23:03, James Kuyper wrote:
> ...
>>>>> Could you explain that a little more? Specifically, could you give an
>>>>> example of how it could cause problems?
>
> You've responded to me twice now, and still have not provided an
> example.

That is a lie. You asked for an example. I gave one.

Plonk.


- Alf

James Kuyper

unread,
Apr 16, 2020, 2:49:22 PM4/16/20
to
OK, that wasn't quite right. You gave me something which you called an
example: foo(-42i16). However, I didn't consider it an example because
you didn't provide any definition for foo(). Without a definition for
foo(), I can't figure out how foo(-42i16) would work any differently
from foo(-fortytwo_16). With a definition, even if I couldn't figure it
out initially, I could at least prove to myself that it does work
differently. Having seen it work differently, I'm sure that I would
eventually figure out why.

Keith Thompson

unread,
Apr 16, 2020, 2:52:35 PM4/16/20
to
Alf, I took a quick look at the posts upthread from this one,
and I didn't see an example from you. It's utterly implausible
that James would lie about something like that, or would have any
reason to. It's possible that there's an honest disagreement about
what constitutes a valid example, or that he overlooked something.

Can you repost the example you're referring to?

--
Keith Thompson (The_Other_Keith) Keith.S.T...@gmail.com
Working, but not speaking, for Philips Healthcare
void Void(void) { Void(); } /* The recursive call of the void */

James Kuyper

unread,
Apr 16, 2020, 3:28:13 PM4/16/20
to
On 4/16/20 2:52 PM, Keith Thompson wrote:
> "Alf P. Steinbach" <alf.p.stein...@gmail.com> writes:
>> On 16.04.2020 17:55, James Kuyper wrote:
>>> On 4/15/20 11:36 PM, Alf P. Steinbach wrote:
>>>> On 16.04.2020 04:40, James Kuyper wrote:
>>>>> On Wednesday, April 15, 2020 at 9:44:31 PM UTC-4, Alf P. Steinbach wrote:
>>>>>> On 15.04.2020 23:03, James Kuyper wrote:
>>> ...
>>>>>>> Could you explain that a little more? Specifically, could you give an
>>>>>>> example of how it could cause problems?
>>>
>>> You've responded to me twice now, and still have not provided an
>>> example.
>>
>> That is a lie. You asked for an example. I gave one.
>>
>> Plonk.
>
> Alf, I took a quick look at the posts upthread from this one,
> and I didn't see an example from you. It's utterly implausible
> that James would lie about something like that, or would have any
> reason to. It's possible that there's an honest disagreement about
> what constitutes a valid example, or that he overlooked something.
>
> Can you repost the example you're referring to?

After the fact, I review his previous posts, and the one immediately
after I asked for an example contained this:

"Since you ask for an example, `foo( -42i16 )` can do the entirely wrong
(unintended) thing, for example if foo copies a value into a memory area
representing a file header, and advances a write position."

Since no definition for foo() is provided, that didn't qualify as what I
would consider a valid example. However, he explicitly labelled it as
one, so Alf obviously disagrees. Because I didn't consider it a valid
example, I quickly forgot about it, and that's why I falsely accused him
of not providing any. I should, instead, have explained why it doesn't
count as a valid example. Just in case he actually has killfiled me,
please let him know what what I just wrote.

The explanation I should have provided follows:

That "example" doesn't provide any basis for me to decide whether or not
his claim has any merit. I could trivially write a function foo() which
matches his description, and is overloaded to do different things for
int16_t and int. That would be a dangerous thing to do, since int16_t
could be a typedef for int. However, if that isn't the case, then
foo(-42i16) would in result in a different overload being called than
would be called by foo(42i16). However, as far as I can tell, the same
would be true of foo(-fourtytwo_16) and foo(fortytwo_16), so I don't see
how this has anything to do with user-defined literals.

If Alf had bothered to provide an example that does show a difference
between foo(-42i16) and foo(-fortytwo_16), then I could have compiled
it, confirmed that it has the behavior he claims, and could then sit
down to try to figure out how that was happening. Without an example, I
am left with my assumption that he's gotten confused about something,
but I'm not sure what.

Alf P. Steinbach

unread,
Apr 16, 2020, 3:57:58 PM4/16/20
to
On 16.04.2020 20:52, Keith Thompson wrote:
> "Alf P. Steinbach" <alf.p.stein...@gmail.com> writes:
>> On 16.04.2020 17:55, James Kuyper wrote:
>>> On 4/15/20 11:36 PM, Alf P. Steinbach wrote:
>>>> On 16.04.2020 04:40, James Kuyper wrote:
>>>>> On Wednesday, April 15, 2020 at 9:44:31 PM UTC-4, Alf P. Steinbach wrote:
>>>>>> On 15.04.2020 23:03, James Kuyper wrote:
>>> ...
>>>>>>> Could you explain that a little more? Specifically, could you give an
>>>>>>> example of how it could cause problems?
>>>
>>> You've responded to me twice now, and still have not provided an
>>> example.
>>
>> That is a lie. You asked for an example. I gave one.
>>
>> Plonk.
>
> Alf, I took a quick look at the posts upthread from this one,
> and I didn't see an example from you. It's utterly implausible
> that James would lie about something like that,

Not as I've known him on Usenet. It's not the first time. I don't plonk
people willy-nilly or lightly; too few people left, everybody counts.


> or would have any
> reason to. It's possible that there's an honest disagreement about
> what constitutes a valid example, or that he overlooked something.
>
> Can you repost the example you're referring to?


Two posts up:
> [Alf wrote] Since you ask for an example, `foo( -42i16 )` can do the
> entirely wrong (unintended) thing, for example if foo copies a value
> into a memory area representing a file header, and advances a write
> position.


When James deliberately set out to misinterpret that as nonsense, then
one post up:
> [Alf wrote] You fail to understand that if `foo` depends on the type
> of the argument, then it depends on the type of the argument. The
> dependency I sketched was how many bytes it would write and advance a
> write position, but there are infinitely many other reasonable such
> dependencies in C++.


- Alf

Vir Campestris

unread,
Apr 16, 2020, 5:00:07 PM4/16/20
to
On 14/04/2020 13:35, Juha Nieminen wrote:
> Chris Vine<chris@cvine--nospam--.freeserve.co.uk> wrote:
>> (He also perpetrated a more serious
>> piece of disinformation with his "const means thread-safe": So
>> std::string::length() is thread-safe is it? I think not - try calling
>> it when another thread is appending to the string.)
> Did he say "thread-safe" or "reentrant"? Because there's a difference
> between the two, and he probably meant the latter (even if he said
> the former). And not as in "const guarantees reentrancy" but
> "in well-designed code const*should* guarantee reentrancy".
>
> (Reentrancy simply means that two threads can call the same funtion
> simultaneously without any misbehavior happening. In other words,
> the function is "thread-safe" with itself, ie. in the context of
> being called from multiple threads at the same time.)
>
> This means effectively that const functions shouldn't have
> side-effects. Even if the function doesn't modify *this, it should
> still have no side-effects (such as calling std::cout, etc).

The problem there is if a const member function alters mutable data
inside the object - say a cache...

Andy

Ike Naar

unread,
Apr 16, 2020, 5:11:18 PM4/16/20
to
On 2020-04-15, Daniel P <daniel...@gmail.com> wrote:
> On Wednesday, April 15, 2020 at 7:13:46 AM UTC-4, Mr Flibble wrote:
>
>> auto is great; use almost everywhere!
>
> Except:
>
> Eigen::Matrix2d mat;
> mat << 1, 2,
> 3, 4;
> Eigen::Matrix2d mat = mat*mat; // NOT HERE

That looks like a redeclaration of 'mat'.

Keith Thompson

unread,
Apr 16, 2020, 5:11:37 PM4/16/20
to
Keith Thompson <Keith.S.T...@gmail.com> writes:
[...]
> Alf, I took a quick look at the posts upthread from this one,
> and I didn't see an example from you. It's utterly implausible
> that James would lie about something like that, or would have any
> reason to. It's possible that there's an honest disagreement about
> what constitutes a valid example, or that he overlooked something.
>
> Can you repost the example you're referring to?

Alf, James has posted a followup. Since you say you've plonked
him, you might not have seen it. Just FYI, I offer no advice.

Keith Thompson

unread,
Apr 16, 2020, 5:19:29 PM4/16/20
to
"Alf P. Steinbach" <alf.p.stein...@gmail.com> writes:
> On 16.04.2020 20:52, Keith Thompson wrote:
[...]
>> Alf, I took a quick look at the posts upthread from this one,
>> and I didn't see an example from you. It's utterly implausible
>> that James would lie about something like that,
>
> Not as I've known him on Usenet. It's not the first time. I don't
> plonk people willy-nilly or lightly; too few people left, everybody
> counts.
>
>> or would have any
>> reason to. It's possible that there's an honest disagreement about
>> what constitutes a valid example, or that he overlooked something.
>>
>> Can you repost the example you're referring to?
>
> Two posts up:
>> [Alf wrote] Since you ask for an example, `foo( -42i16 )` can do the
>> entirely wrong (unintended) thing, for example if foo copies a value
>> into a memory area representing a file header, and advances a write
>> position.

He has addressed that.

> When James deliberately set out to misinterpret that as nonsense,

Your use of the word "deliberately" suggests an insight into his
thinking that I do not believe you possess.

[...]

Daniel P

unread,
Apr 16, 2020, 5:50:09 PM4/16/20
to
On Thursday, April 16, 2020 at 5:11:18 PM UTC-4, Ike Naar wrote:
> On 2020-04-15, Daniel P wrote:
> >
> > Eigen::Matrix2d mat;
> > mat << 1, 2,
> > 3, 4;
> > Eigen::Matrix2d mat = mat*mat; // NOT HERE
>
> That looks like a redeclaration of 'mat'.

Ah, should be

Eigen::Matrix2d mat2 = mat*mat; // NOT HERE

but the point remains the same, auto can't be used here.

Daniel





Alf P. Steinbach

unread,
Apr 16, 2020, 5:50:28 PM4/16/20
to
On 16.04.2020 23:19, Keith Thompson wrote:
> "Alf P. Steinbach" <alf.p.stein...@gmail.com> writes:
>> On 16.04.2020 20:52, Keith Thompson wrote:
> [...]
>>> Alf, I took a quick look at the posts upthread from this one,
>>> and I didn't see an example from you. It's utterly implausible
>>> that James would lie about something like that,
>>
>> Not as I've known him on Usenet. It's not the first time. I don't
>> plonk people willy-nilly or lightly; too few people left, everybody
>> counts.
>>
>>> or would have any
>>> reason to. It's possible that there's an honest disagreement about
>>> what constitutes a valid example, or that he overlooked something.
>>>
>>> Can you repost the example you're referring to?
>>
>> Two posts up:
>>> [Alf wrote] Since you ask for an example, `foo( -42i16 )` can do the
>>> entirely wrong (unintended) thing, for example if foo copies a value
>>> into a memory area representing a file header, and advances a write
>>> position.
>
> He has addressed that.

He may have tried to the best of his ability.

But being incompetent in an area of expertise, does not exonerate him
from lying.


>> When James deliberately set out to misinterpret that as nonsense,
>
> Your use of the word "deliberately" suggests an insight into his
> thinking that I do not believe you possess.
>
> [...]

You're right, sorry.

- Alf

Keith Thompson

unread,
Apr 16, 2020, 7:16:31 PM4/16/20
to
"Alf P. Steinbach" <alf.p.stein...@gmail.com> writes:
> On 16.04.2020 23:19, Keith Thompson wrote:
[...]
>> He has addressed that.
>
> He may have tried to the best of his ability.
>
> But being incompetent in an area of expertise, does not exonerate him
> from lying.

You've chosen not to read what he has to say, which is of course
your right. But accusing him of lying without paying attention
to his response seems a bit much. And again, the word "lying"
implies deliberate intent, which requires insight into his state
of mind that I presume you do not possess.

If I were in your position, I would either drop this or see what
James has to say before replying.

Alf P. Steinbach

unread,
Apr 16, 2020, 7:33:50 PM4/16/20
to
On 17.04.2020 01:16, Keith Thompson wrote:
> "Alf P. Steinbach" <alf.p.stein...@gmail.com> writes:
>> On 16.04.2020 23:19, Keith Thompson wrote:
> [...]
>>> He has addressed that.
>>
>> He may have tried to the best of his ability.
>>
>> But being incompetent in an area of expertise, does not exonerate him
>> from lying.
>
> You've chosen not to read what he has to say, which is of course
> your right. But accusing him of lying without paying attention
> to his response seems a bit much. And again, the word "lying"
> implies deliberate intent, which requires insight into his state
> of mind that I presume you do not possess.
>
> If I were in your position, I would either drop this or see what
> James has to say before replying.

Postings after being plonked area often compelling, something that
appears one just has to respond to.

That's part of the reason for the plong in the first place.

I refuse to be drawn into more flame-baits from him. He lied. EOS.


- Alf

Chris Vine

unread,
Apr 16, 2020, 8:15:52 PM4/16/20
to
However you have track record on this. You plonked me many (probably
getting on for 10) years ago for disagreeing with you on something on
which you were completely wrong. It was about whether std::array
objects counted as an 'array' (as that word is defined in italics) in
the standard. You may well remember it but I am not going to look back
on dates or even on the discussion because it was so pathetic. However
you worked yourself into a lather and concluded that this was a matter
for which "plinking" was the appropriate remedy. You then proceeded
for a few weeks to post lists of the people who were "known liers" who
you had plonked - or "plinked", a primitive version of what is now
called doxxing.

You disappeared not long after that, and came back after a couple of
years as if from new and much improved. Try not to descend into the pit
of madness again. The problem is that your definition of "lie" is
"Mummy its not fair he disagreed with me".

I may well still be plonked for all I know. Dunno. You can tell me.

Alf P. Steinbach

unread,
Apr 16, 2020, 10:18:42 PM4/16/20
to
Not sure what your fantasy is about, but you're revealing yourself as
not nice person.


- ALf

Öö Tiib

unread,
Apr 17, 2020, 3:07:31 AM4/17/20
to
People can be mistaken. That is not equal to lying. Lying is when
people (at least think that they) know what the truth is but tell
opposite. I do not understand how you detected that it was lying
there.

James Kuyper is quite honest person IMHO so your diagnose is likely
groundless. However code like "auto x = -42i16;" resulting with x
being int is indeed very strong illusion. Even most advanced
programmers will fall under it if they do not read code carefully
like Stalker in Zone. Also I have found no tool or IDE that warns
about it. So I do not understand why James Kuyper does not
find it to be a bear trap in language.



Keith Thompson

unread,
Apr 17, 2020, 5:34:25 AM4/17/20
to
Öö Tiib <oot...@hot.ee> writes:
[...]
> James Kuyper is quite honest person IMHO so your diagnose is likely
> groundless. However code like "auto x = -42i16;" resulting with x
> being int is indeed very strong illusion. Even most advanced
> programmers will fall under it if they do not read code carefully
> like Stalker in Zone. Also I have found no tool or IDE that warns
> about it. So I do not understand why James Kuyper does not
> find it to be a bear trap in language.

It's not a *new* bear trap specific to user-defined literals.

short s = 42; // type short
auto minus_s = -s; // type int

Chris Vine

unread,
Apr 17, 2020, 7:17:14 AM4/17/20
to
On Fri, 17 Apr 2020 04:18:32 +0200
"Alf P. Steinbach" <alf.p.stein...@gmail.com> wrote:
[snip]
> Not sure what your fantasy is about, but you're revealing yourself as
> not nice person.

I suppose in retrospect my remark about Mummy probably was unnecessary,
so in that particular respect I apologize. However, calling someone
out as a liar when they are at worst mistaken isn't so nice either.
Also, if you want to plonk someone then do so, but you don't need to
announce it to the world in order to obtain your satisfaction.

Furthermore, for what it is worth you were a considerable pest in 2013
(as I have now looked it up to be). In the space of about one month you
"plinked"[1] or "plonked" a significant proportion of the people now or
recently on this newsgroup, including me, Daniel P, Paavo Helde, Scott
Lurndal, Tobias Mueller (not sure if he still posts here), Flibble (aka
Leigh Johnston) and David Brown, and many others no longer here,
including Victor Bazarov.

And because one "plinking" wasn't quite enough for you, you took the
opportunities available to you to publish your kill list to make sure
that those you had "plinked" were suitably annoyed once more.

***************************

[1] You gave a rather lavatorial description of this word. Your
explanation was in these terms:

"'plink' is (so it was explained to me) the sound of a light-weight
troll hitting the inner surface of the toilet bowl, while 'plonk' is
the sound of a more heavy-weight troll being flushed down"

Juha Nieminen

unread,
Apr 17, 2020, 7:32:42 AM4/17/20
to
Vir Campestris <vir.cam...@invalid.invalid> wrote:
>> Did he say "thread-safe" or "reentrant"? Because there's a difference
>> between the two, and he probably meant the latter (even if he said
>> the former). And not as in "const guarantees reentrancy" but
>> "in well-designed code const*should* guarantee reentrancy".
>>
>> (Reentrancy simply means that two threads can call the same funtion
>> simultaneously without any misbehavior happening. In other words,
>> the function is "thread-safe" with itself, ie. in the context of
>> being called from multiple threads at the same time.)
>>
>> This means effectively that const functions shouldn't have
>> side-effects. Even if the function doesn't modify *this, it should
>> still have no side-effects (such as calling std::cout, etc).
>
> The problem there is if a const member function alters mutable data
> inside the object - say a cache...

Hence, well-designed code should kind of make "const" (member function)
a guarantee for reentrancy, and thus shouldn't be modifying any mutable
data. If it needs to modify something, it shouldn't claim being "const".

(I understand that there may be practical situations where a member
function has to be const while having side-effects, but at least it
should be a goal to aim for.)

Paavo Helde

unread,
Apr 17, 2020, 8:31:05 AM4/17/20
to
In this case the class should take care to apply the needed internal
synchronization by itself. The calling code should not need to know or
care about such implementation details.

Jorgen Grahn

unread,
Apr 17, 2020, 8:58:14 AM4/17/20
to
On Fri, 2020-04-17, Öö Tiib wrote:
...
> Even most advanced
> programmers will fall under it if they do not read code carefully
> like Stalker in Zone.

Thanks for reminding me! I think I'll tie a rag to a heavy nut and
keep it at my desk at work, as an obscure reference. It could
probably come in handy during sprint planning ...

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

Öö Tiib

unread,
Apr 17, 2020, 9:01:12 AM4/17/20
to
On Friday, 17 April 2020 12:34:25 UTC+3, Keith Thompson wrote:
> Öö Tiib <oot...@hot.ee> writes:
> [...]
> > James Kuyper is quite honest person IMHO so your diagnose is likely
> > groundless. However code like "auto x = -42i16;" resulting with x
> > being int is indeed very strong illusion. Even most advanced
> > programmers will fall under it if they do not read code carefully
> > like Stalker in Zone. Also I have found no tool or IDE that warns
> > about it. So I do not understand why James Kuyper does not
> > find it to be a bear trap in language.
>
> It's not a *new* bear trap specific to user-defined literals.
>
> short s = 42; // type short
> auto minus_s = -s; // type int

Certainly so and that is also illusion but it is easier to notice it.
We read it as "minus s" and it can be easily learned that minus may
change type.

auto x = -42i16; // type is int

That however we read "minus forty two as int 16" and so there it is
way simpler to fall under illusion that it was handy way to write
int16_t(-42) and not to notice that it is actually -(int16_t)42.


James Kuyper

unread,
Apr 17, 2020, 9:42:02 AM4/17/20
to
On 4/17/20 3:05 AM, Öö Tiib wrote:
...
> People can be mistaken. That is not equal to lying. Lying is when

I made a mistake when I said that he'd failed to respond to my request
for an example. I'd forgotten that he gave something he considered to be
an example, because it was, IMO, far too incomplete to qualify as one.
But I should have mentioned the inadequacies of his "example", rather
than claimed that he didn't provide one.

> people (at least think that they) know what the truth is but tell
> opposite. I do not understand how you detected that it was lying
> there.
>
> James Kuyper is quite honest person IMHO so your diagnose is likely
> groundless. However code like "auto x = -42i16;" resulting with x
> being int is indeed very strong illusion. Even most advanced
> programmers will fall under it if they do not read code carefully
> like Stalker in Zone. Also I have found no tool or IDE that warns
> about it. So I do not understand why James Kuyper does not
> find it to be a bear trap in language.

I never suggested that it wasn't a problem. My point has only been that
it's not a new problem related to user-defined literals. You get exactly
the same problem with

auto x = -fortytwo_16;

It's a very old problem due to the integral promotion rules, and as such
it predates C++, going all the way back to the early days of C.
Experienced C and C++ programmers are necessarily used to this problem,
and know how to deal with it.

Ben Bacarisse

unread,
Apr 17, 2020, 11:34:43 AM4/17/20
to
嘱 Tiib <oot...@hot.ee> writes:

> On Friday, 17 April 2020 12:34:25 UTC+3, Keith Thompson wrote:
It may be simpler to fall into the trap in that case, but there are so
many others (~42_i16, 42_i16 + 10, 42_i16 << 1, printf("...", 42_i26))
that knowing the underlying truth really helps.

--
Ben.

Alf P. Steinbach

unread,
Apr 17, 2020, 12:26:26 PM4/17/20
to
On 17.04.2020 13:32, Juha Nieminen wrote:
> Vir Campestris <vir.cam...@invalid.invalid> wrote:
>>> Did he say "thread-safe" or "reentrant"? Because there's a difference
>>> between the two, and he probably meant the latter (even if he said
>>> the former). And not as in "const guarantees reentrancy" but
>>> "in well-designed code const*should* guarantee reentrancy".
>>>
>>> (Reentrancy simply means that two threads can call the same funtion
>>> simultaneously without any misbehavior happening. In other words,
>>> the function is "thread-safe" with itself, ie. in the context of
>>> being called from multiple threads at the same time.)
>>>
>>> This means effectively that const functions shouldn't have
>>> side-effects. Even if the function doesn't modify *this, it should
>>> still have no side-effects (such as calling std::cout, etc).
>>
>> The problem there is if a const member function alters mutable data
>> inside the object - say a cache...
>
> Hence, well-designed code should kind of make "const" (member function)
> a guarantee for reentrancy, and thus shouldn't be modifying any mutable
> data. If it needs to modify something, it shouldn't claim being "const".

It's /possible/ to access cached data (which I envision as the main use
case here) in a very efficient thread-safe way, as exemplified by common
implementations of local static variable initialization.

However that safety must designed in, and that work is a cost.

So far, for non-threaded code, which is the majority, differentiating
between logical `const`-ness and deep complete `const`-ness has been
useful, and without much added work for mere logical `const`-ness.


> (I understand that there may be practical situations where a member
> function has to be const while having side-effects, but at least it
> should be a goal to aim for.)

Maybe some way to see if some code is "designed for threading", or in
the other direction, "unsafe for threading"...


- Alf

Öö Tiib

unread,
Apr 17, 2020, 8:42:50 PM4/17/20
to
On Friday, 17 April 2020 18:34:43 UTC+3, Ben Bacarisse wrote:
> Öö Tiib <oot...@hot.ee> writes:
>
> > On Friday, 17 April 2020 12:34:25 UTC+3, Keith Thompson wrote:
What really helps is to demand writing static_cast<int16_t>(-42) and to
forbid usage of confusing user-defined integral literal operators of C++.
Basically same as with octals. Useless, broken feature. Inevitably
someone messes it up with "maintenance" and then wastes half day of
his own wondering plus 15 minutes of mine too.


Christian Gollwitzer

unread,
Apr 18, 2020, 1:14:32 PM4/18/20
to
Am 17.04.20 um 17:34 schrieb Ben Bacarisse:
> Öö Tiib <oot...@hot.ee> writes:
>>
>> auto x = -42i16; // type is int
>>
>> That however we read "minus forty two as int 16" and so there it is
>> way simpler to fall under illusion that it was handy way to write
>> int16_t(-42) and not to notice that it is actually -(int16_t)42.
>
> It may be simpler to fall into the trap in that case, but there are so
> many others (~42_i16, 42_i16 + 10, 42_i16 << 1, printf("...", 42_i26))
> that knowing the underlying truth really helps.

I've designed a Matlab-like language, and the integer promotion rules
also got me thinking. In the end I concluded that the base integer type
(like int in C++) should be disctinct from the fixed-widths types and
treated as an ideal integer. i.e., even if int== int32_t, the type
promotion should not widen smaller types in this case.

Therefore (in my language):

auto x = -42i16;
// x is -42i16
// unary - is defined for int16_t -> int16_t

auto i = x + 1
// i is -41i16
// no promotion, because int is special

auto j = x + 1i32;
// j is -41i32
// promoted because int32_t has more bits than int16_t

uint8_t byte=0;
auto t = byte - 1;
// t is 255u8

auto u = byte - 1i32;
// u is -1i32

Christian

Vir Campestris

unread,
Apr 18, 2020, 4:53:27 PM4/18/20
to
On 17/04/2020 14:41, James Kuyper wrote:
> I never suggested that it wasn't a problem. My point has only been that
> it's not a new problem related to user-defined literals. You get exactly
> the same problem with
>
> auto x = -fortytwo_16;
>
> It's a very old problem due to the integral promotion rules, and as such
> it predates C++, going all the way back to the early days of C.
> Experienced C and C++ programmers are necessarily used to this problem,
> and know how to deal with it.

Interesting. I think I count myself as experienced - I've been writing C
and C++ for nearly 40 years - and yet it's never bitten me.

probably because
short plus = 42;
short minus = -plus;

tends to end up as something like

load 16 into register
store into plus as 16 bit
negate
store into minus as 16 bit

This case where
auto minus = -plus;
results in a 32 bit (or more!) size for minus is to me just another
reason not to use auto where I can avoid it.

Andy

Öö Tiib

unread,
Apr 18, 2020, 4:59:40 PM4/18/20
to
Note that we can precisely enforce what mutations of 16 bit type
happen and when (and with how explicit syntax) in C++, too. However
we then have to make fully custom 16 bit types and to specify all
the operators for those.

C++ lets us to have types like "enum class I16 : int16_t {};"
or like "struct I16 {int16_t v_;};" and army of operators.
Both also have their (different) benefits, disadvantages and
bear traps attached. For example with enum we can't have
constructors, conversion operators, operator=, operator[],
operator() or operator-> overloaded while with struct its
layout-compatibility with int16_t or with enums that
have int16_t as underlying type is far dimmer in standard.



James Kuyper

unread,
Apr 19, 2020, 12:22:38 PM4/19/20
to
On 4/18/20 4:53 PM, Vir Campestris wrote:
> On 17/04/2020 14:41, James Kuyper wrote:
>> I never suggested that it wasn't a problem. My point has only been that
>> it's not a new problem related to user-defined literals. You get exactly
>> the same problem with
>>
>> auto x = -fortytwo_16;
>>
>> It's a very old problem due to the integral promotion rules, and as such
>> it predates C++, going all the way back to the early days of C.
>> Experienced C and C++ programmers are necessarily used to this problem,
>> and know how to deal with it.
>
> Interesting. I think I count myself as experienced - I've been writing C
> and C++ for nearly 40 years - and yet it's never bitten me.

I didn't say it was a big problem, either.

> probably because
> short plus = 42;
> short minus = -plus;
>
> tends to end up as something like
>
> load 16 into register
> store into plus as 16 bit
> negate
> store into minus as 16 bit
>
> This case where
> auto minus = -plus;
> results in a 32 bit (or more!) size for minus is to me just another
> reason not to use auto where I can avoid it.

Well, the key reason why the integer promotions are generally not a
serious problems is that they are always value-preserving (unlike the
usual arithmetic conversions, where a negative value can sometimes end
up being converted to an unsigned type). That's what makes those
optimizations legal.

Still, there is one situation where the integral promotions can be
problematic. Expressions of unsigned type whose valid values are all
representable as 'int' will get promoted to a int. In itself that's not
a problem, but it can result in unexpected use of signed arithmetic on
values that started out as unsigned. That's why I so surprised to see
someone complain about the integral promotion of int16_t to int; I'd be
much less surprised to see a complaint about uint16_t being promoted to int.


0 new messages