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

strange issue ...

207 views
Skip to first unread message

Bonita Montero

unread,
Oct 3, 2019, 2:30:08 AM10/3/19
to
Why doesn't this work?

struct A
{
int const ABC = 123;

struct B
{
int f();
};
};

int A::B::f()
{
return ABC;
}

If I make ABC static it works, but is there another way without making
ABC static?

Melzzzzz

unread,
Oct 3, 2019, 2:48:35 AM10/3/19
to
On 2019-10-03, Bonita Montero <Bonita....@gmail.com> wrote:
> Why doesn't this work?

Because C++ is not Java...
Pass this pointer to constructor of B, save it in member and refer to it
later...




--
press any key to continue or any other to quit...
U ničemu ja ne uživam kao u svom statusu INVALIDA -- Zli Zec
Na divljem zapadu i nije bilo tako puno nasilja, upravo zato jer su svi
bili naoruzani. -- Mladen Gogala

Paavo Helde

unread,
Oct 3, 2019, 2:49:59 AM10/3/19
to
On 3.10.2019 9:29, Bonita Montero wrote:
> Why doesn't this work?
>
> struct A
> {
> int const ABC = 123;

This instructs the compiler that whenever an A object is created, it
should contain an integer member with value 123. Or at least by the
as-if rule, the program should behave as if each time an A object was
created it would appear to hold an integer member with value 123.

>
> struct B
> {
> int f();
> };
> };
>
> int A::B::f()
> {
> return ABC;
> }
>
> If I make ABC static it works, but is there another way without making
> ABC static?

So how should it work in your mind?

int main() {
A::B b;
return b.f();
}

There is no A object created anywhere, so there is no existing ABC
member to read and return, so the program cannot physically work. If
this program compiled and returned 123, it would be a bug in an
over-eager optimizer as there is no equivalent non-optimized program.


Bonita Montero

unread,
Oct 3, 2019, 5:13:54 AM10/3/19
to
>> If I make ABC static it works, but is there another way without making
>> ABC static?

> Pass this pointer to constructor of B, save it in member and refer to it
> later...

LOL.

Bonita Montero

unread,
Oct 3, 2019, 5:16:14 AM10/3/19
to
> There is no A object created anywhere, so there is no existing ABC
> member to read and return, so the program cannot physically work. ...

I know the reasons why this doesn't work.
I only wanted to have a workaround without making ABC static.

Juha Nieminen

unread,
Oct 3, 2019, 5:46:27 AM10/3/19
to
Why don't you want it to be static? A static const integer type
doesn't require a separate definition. What are the downsides
you are seeing?

Sam

unread,
Oct 3, 2019, 7:00:43 AM10/3/19
to
There is no "workaround". C++ does not work this way. If something is a
class member, it can only exist as an instance of the class. Therefore you
can only access it as a member of an instance of the class. It is not an
independent object, with a life of its own. End of story.

Paavo Helde

unread,
Oct 3, 2019, 7:36:10 AM10/3/19
to
If you want a workaround for a physically impossible thing, then it
shows you do not really understand why it does not work.

Otherwise, if there are additional guarantees, e.g. if the program
guarantees that a B object physically resides inside an A object at a
fixed offset, then indeed there are ugly workarounds to access the
fields of the enclosing object, involving offsetof() and such. A proper
way to do such things is to derive A from B and to use static_cast
instead of offsetof.

But this makes lots of assumptions about your actual problem, which you
have not cared to disclose.

James Kuyper

unread,
Oct 3, 2019, 9:25:59 AM10/3/19
to
On Thursday, October 3, 2019 at 5:16:14 AM UTC-4, Bonita Montero wrote:
> > There is no A object created anywhere, so there is no existing ABC
> > member to read and return, so the program cannot physically work. ...
>
> I know the reasons why this doesn't work.

If so, then it was counter-productive to start your first message in
this thread with the question "Why doesn't this work?"

> I only wanted to have a workaround without making ABC static.

Well, the simple, obvious solution is to make ABC static. I can't
imagine why it's important to you that it not be static. If you do have
a good reason for that requirement, you should explain the reason, so
people can better help you.

In the meantime, the following approach meets your stated requirement:

int const ABC = 123;

struct A
{
struct B
{
int f();
};
};

So does the following:

struct A
{
struct B
{
int const ABC = 123;
int f();
};
};

But those solutions are so obvious that I assume that they violate some
additional requirements that you failed to mention. If so, you need to
explain all the relevant requirements, so people will be better able to
help you.

Bonita Montero

unread,
Oct 3, 2019, 9:41:08 AM10/3/19
to
> So does the following:
>
> struct A
> {
> struct B
> {
> int const ABC = 123;
> int f();
> };
> };
>
> But those solutions are so obvious that I assume that they violate some
> additional requirements that you failed to mention. If so, you need to
> explain all the relevant requirements, so people will be better able to
> help you.

I need the constant in the outer class.
With static I found a bug in the gcc, that I couln't use the constant in
the constructor. The constants were defined this way:

static
std::uint32_t const UNPINNED_NODE = 0,
FREE_NODE = 0xFFFFFFFFu;


I assigned FREE_NODE to a member in this way.

nd->pinCounter = (uint32_t const)FREE_NODE; // gcc-bug?

Without the cast I got a linker-error (MSVC compiled the code).
Surprisingly all other "accesses" to FREE_NODE in the outer class
didn't give this linker-error. I don't know why gcc acesses FREE_NODE
like it's not a const at this point in the constructor. So I asked for
a work-around bcause I needed the constant in the innner class as well
in the outer.

Paavo Helde

unread,
Oct 3, 2019, 10:02:16 AM10/3/19
to

Bonita Montero

unread,
Oct 3, 2019, 10:07:57 AM10/3/19
to
That's a different problem because it's not about static const members.
I tried defining the member within one translation-unit, but this didn't
help. And if I removed the line I quoted accessed to FREE_NODE by ohter
methods of my class weren't complained by gcc.

Bonita Montero

unread,
Oct 3, 2019, 10:13:20 AM10/3/19
to
>> https://isocpp.org/wiki/faq/ctors#static-const-with-initializers
>> And no, it's not a bug in gcc.

> I tried defining the member within one translation-unit, but this didn't
> help. And if I removed the line I quoted accessed to FREE_NODE by ohter
> methods of my class weren't complained by gcc.

And read-access to FREE_NODE is compile-time constant anyway. Only if
I nonsensically cast FREE_NODE to non-const and assign ít a value there
should be a linker-error.

Paavo Helde

unread,
Oct 3, 2019, 10:19:03 AM10/3/19
to
On 3.10.2019 17:07, Bonita Montero wrote:
>> Your problem is explained here:
>> https://isocpp.org/wiki/faq/ctors#link-errs-static-data-mems
>
> That's a different problem because it's not about static const members.
>
>> https://isocpp.org/wiki/faq/ctors#static-const-with-initializers

The second link is specifically about static const members. Quoting from
there:

"And, just like other static data members, it must be defined in exactly
one compilation unit, though this time without the = initializer part"

>> And no, it's not a bug in gcc.
>
> I tried defining the member within one translation-unit, but this didn't
> help.

Then you did something wrong.

> And if I removed the line I quoted accessed to FREE_NODE by ohter
> methods of my class weren't complained by gcc.

Such linker related bugs in your code are "no diagnostic required".

Bonita Montero

unread,
Oct 3, 2019, 10:36:53 AM10/3/19
to
> The second link is specifically about static const members. Quoting from
> there:
> "And, just like other static data members, it must be defined in exactly
> one compilation unit, though this time without the = initializer part"

That's not alway true. The have to be declared only if they're written
or read. But if they're compile-time-constant like my uint32_t, they
are neither.

>> I tried defining the member within one translation-unit, but this didn't
>> help.

> Then you did something wrong.

No, I didn't

>> And if I removed the line I quoted accessed to FREE_NODE by ohter
>> methods of my class weren't complained by gcc.

> Such linker related bugs in your code are "no diagnostic required".

No, that's a clear sign that gcc has a bug at this point. At other
placess FREE_NODE is compile-time-constant but in the constructor
it is physically accessed.

Bonita Montero

unread,
Oct 3, 2019, 11:15:35 AM10/3/19
to
// Fred.h
class Fred {
public:
private:
static int j_;
};

// Fred.cpp
#include "Fred.h"
int Fred::j_ = some_expression_evaluating_to_an_int;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
That's not ncessary because Fred::j_ is compile-time constant.

Bo Persson

unread,
Oct 3, 2019, 11:28:39 AM10/3/19
to
No, the bug is in your code.

The separate definition is formally required (at least in older C++).

However, the optimizer might often remove all references to the
constant, and then the linker will not notice that it is missing and let
you get away with it.

This case is so common that most people probably belive it is the norm.


Bo Persson

Bonita Montero

unread,
Oct 3, 2019, 11:32:49 AM10/3/19
to
>> No, that's a clear sign that gcc has a bug at this point. At other
>> placess FREE_NODE is compile-time-constant but in the constructor
>> it is physically accessed.

> No, the bug is in your code.

> The separate definition is formally required (at least in older C++).

No, the separate definition is not required if it can be evaluated to
a compile-time constant. You can even define arrays with compile-time
sizes on those const-members.

Bonita Montero

unread,
Oct 3, 2019, 11:36:05 AM10/3/19
to
> However, the optimizer might often remove all references to the
> constant, and then the linker will not notice that it is missing
> and let you get away with it.

Consider this:

int const ABC = 123;

int fABC()
{
return ::ABC;
}

If I compile it without optimizations with MSVC++, I'll get this:

mov eax, 123
ret 0

And gcc compiles this without optimizations:

pushq %rbp
movq %rsp, %rbp
movl $123, %eax
popq %rbp
ret

Bo Persson

unread,
Oct 3, 2019, 11:46:21 AM10/3/19
to
Quoting myself:

"However, the optimizer might often remove all references to the
constant, and then the linker will not notice that it is missing
and let you get away with it. "


In C++17 you can use constexpr instead of const, and formally get away
from the requirement for a separate definition.

You might want to read the last paragraph on this page:

https://en.cppreference.com/w/cpp/language/static

"If a static data member is declared constexpr, it is implicitly inline
and does not need to be redeclared at namespace scope. This
redeclaration without an initializer (formerly required as shown above)
is still permitted, but is deprecated. (since C++17)"



Bo Persson

Bonita Montero

unread,
Oct 3, 2019, 12:55:52 PM10/3/19
to
> Quoting myself:
> "However, the optimizer might often remove all references to the
> constant, and then the linker will not notice that it is missing
> and let you get away with it. "

No, that's not a matter of the optimizer. The language itself imposes
that a const variable has to be compile-time constant unter certain
cirtcumstances. Without that the following wouldn't be possible:

size_t const ARRAY_SIZE = 123;
int array[ARRAY_SIZE];

How could that be possible if this would be a matter of the optimizer?

Paavo Helde

unread,
Oct 3, 2019, 1:32:14 PM10/3/19
to
Exactly, this line is the *definition* of a constant, providing a linker
symbol if needed. What you can have inside a class is just a *declaration*.

If you acknowledge the above ARRAY_SIZE definition as valid code, then
why do you refuse to provide a similar definition for your const static
class member?


Bonita Montero

unread,
Oct 3, 2019, 1:35:23 PM10/3/19
to
>>> Quoting myself:
>>> "However, the optimizer might often remove all references to the
>>> constant, and then the linker will not notice that it is missing
>>> and let you get away with it. "

>> No, that's not a matter of the optimizer. The language itself imposes
>> that a const variable has to be compile-time constant unter certain
>> cirtcumstances. Without that the following wouldn't be possible:
>>
>>      size_t const ARRAY_SIZE = 123;
>>      int array[ARRAY_SIZE];
>> How could that be possible if this would be a matter of the optimizer?

> Exactly, this line is the *definition* of a constant, providing a linker
> symbol if needed. What you can have inside a class is just a *declaration*.

The actual discussion was whether the optimization is a matter of the
language or of the optimizer. We were not discussion definitions vs.
declarations at this point.

> If you acknowledge the above ARRAY_SIZE definition as valid code, then
> why do you refuse to provide a similar definition for your const static
> class member?

Because it is not necessary from what the language allows.

Paavo Helde

unread,
Oct 3, 2019, 1:50:20 PM10/3/19
to
On 3.10.2019 20:35, Bonita Montero wrote:
>>>> Quoting myself:
>>>> "However, the optimizer might often remove all references to the
>>>> constant, and then the linker will not notice that it is missing
>>>> and let you get away with it. "
>
>>> No, that's not a matter of the optimizer. The language itself imposes
>>> that a const variable has to be compile-time constant unter certain
>>> cirtcumstances. Without that the following wouldn't be possible:
>>>
>>> size_t const ARRAY_SIZE = 123;
>>> int array[ARRAY_SIZE];
>>> How could that be possible if this would be a matter of the optimizer?
>
>> Exactly, this line is the *definition* of a constant, providing a
>> linker symbol if needed. What you can have inside a class is just a
>> *declaration*.
>
> The actual discussion was whether the optimization is a matter of the
> language or of the optimizer. We were not discussion definitions vs.
> declarations at this point.

Too bad, because those are the cause of your original problems.

Anyway, optimizations do not change the correctness of a program. Your
program is still invalid if you do not provide definitions for needed
entities. The fact that some compilers do not produce errors when
compiling such an invalid program is beside the point and does not make
your program correct. The compilers are not obliged to detect these
errors because the standard allows them to skip those checks (by saying
"no diagnostic required").

>
>> If you acknowledge the above ARRAY_SIZE definition as valid code, then
>> why do you refuse to provide a similar definition for your const
>> static class member?
>
> Because it is not necessary from what the language allows.

Exactly the opposite, a definition is necessary because the language,
ie. the C++ standard, requires it. Some implementations of the language
might sometimes be more lax, but one should not count on that.


Bonita Montero

unread,
Oct 3, 2019, 1:53:30 PM10/3/19
to
>> The actual discussion was whether the optimization is a matter of the
>> language or of the optimizer. We were not discussion definitions vs.
>> declarations at this point.

> Too bad, because those are the cause of your original problems.

No, that was simply a compiler bug. The language allows read-access to
const'ed variables without definition if they're evaluatable at compile
-time.

> Anyway, optimizations do not change the correctness of a program.

AAAAAAAAAAAAH!
What the compiler is imposed to do here isn't an optimization.


> The fact that some compilers do not produce errors when
> compiling such an invalid program ...

My program isn't invalid. A const-variable evaluatable at compile-time
hasn't to be defined!!!

> Exactly the opposite, a definition is necessary because the language,
> ie. the C++ standard, requires it.

No.

Paavo Helde

unread,
Oct 3, 2019, 2:11:16 PM10/3/19
to
On 3.10.2019 20:53, Bonita Montero wrote:
>
>> Exactly the opposite, a definition is necessary because the language,
>> ie. the C++ standard, requires it.
>
> No.
>

From https://en.cppreference.com/w/cpp/language/static :

"If a const non-inline (since C++17) static data member or a constexpr
static data member (since C++11) is odr-used, a definition at namespace
scope is still required, but it cannot have an initializer. This
definition is deprecated for constexpr data members (since C++17)."

What's tricky is to see where in your program you have odr-use. For
example, passing the data member to a function via a const reference
qualifies as an odr-use. I guess that's what happened in your example.



Bonita Montero

unread,
Oct 3, 2019, 2:21:37 PM10/3/19
to
> "If a const non-inline (since C++17) static data member or a constexpr
> static data member (since C++11) is odr-used, a definition at namespace
> scope is still required, but it cannot have an initializer. This
> definition is deprecated for constexpr data members (since C++17)."

But it's not really "odr-used" because a const-variable can be compile
-time constant.

Bonita Montero

unread,
Oct 3, 2019, 2:24:18 PM10/3/19
to
Look at the example:
https://en.cppreference.com/w/cpp/language/definition#ODR-use

struct S {
static const int x = 0; // static data member
// a definition outside of class is required if it is odr-used
};
const int& f(const int& r);

int n = b ? (1, S::x) // S::x is not odr-used here
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ !!!
: f(S::x); // S::x is odr-used here: a definition is required

Keith Thompson

unread,
Oct 3, 2019, 2:30:47 PM10/3/19
to
Bonita Montero <Bonita....@gmail.com> writes:
[...]
> No, that was simply a compiler bug. The language allows read-access to
> const'ed variables without definition if they're evaluatable at compile
> -time.
[...]

If you've encountered a compiler bug, can you provide a self-contained
test case that demonstrates it? Are you saying that the code in the
first article in this thread is such an example?

(I do not give permission to quote this article without a proper
attribution line.)

--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
Will write code for food.
void Void(void) { Void(); } /* The recursive call of the void */

Paavo Helde

unread,
Oct 3, 2019, 2:40:11 PM10/3/19
to
On 3.10.2019 21:24, Bonita Montero wrote:
>
> Look at the example:
>...
> : f(S::x); // S::x is odr-used here: a definition is required
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Paavo Helde

unread,
Oct 3, 2019, 2:40:41 PM10/3/19
to
Irrelevant.

Bonita Montero

unread,
Oct 3, 2019, 2:46:11 PM10/3/19
to
>>
>> Look at the example:
> >...
>>            : f(S::x);  // S::x is odr-used here: a definition is required
>                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Yes, because f is given a reference!

Bonita Montero

unread,
Oct 3, 2019, 2:47:03 PM10/3/19
to
No, not irrelevant. A definition isn't necessary here and what you
quoted from cppreference.com exactly says that.

Bonita Montero

unread,
Oct 3, 2019, 2:49:38 PM10/3/19
to
> If you've encountered a compiler bug, can you provide a self-contained
> test case that demonstrates it? Are you saying that the code in the
> first article in this thread is such an example?

I don't need a test. The same const-value is taken from other methods
of the class without any linker-errors when I strip the line of the
constructor. I take the value from this const-"variable" in the same
way in the constructor and the linker errors a missing definition..

> (I do not give permission to quote this article without a proper
> attribution line.)

LOL, idiot!

Paavo Helde

unread,
Oct 3, 2019, 3:16:12 PM10/3/19
to
On 3.10.2019 21:49, Bonita Montero wrote:
>> If you've encountered a compiler bug, can you provide a self-contained
>> test case that demonstrates it? Are you saying that the code in the
>> first article in this thread is such an example?
>
> I don't need a test.

You will need to provide a self-contained test case because without that
nobody will believe you have discovered a bug in gcc. A bug in your own
code is much more likely.


Bonita Montero

unread,
Oct 3, 2019, 3:20:21 PM10/3/19
to
>> I don't need a test.

> You will need to provide a self-contained test case because without that
> nobody will believe you have discovered a bug in gcc. A bug in your own
> code is much more likely.

I don't need a test because I have a workaround: when I cast
FREE_NODE to its own type, i.e. (uint32_t const), then I don't
get an error anymore. That's simply a bug.

Paavo Helde

unread,
Oct 3, 2019, 3:28:40 PM10/3/19
to
You are changing it from lvalue to rvalue, which can make a lot of
difference, for example when binding to a reference.

Bonita Montero

unread,
Oct 3, 2019, 3:32:06 PM10/3/19
to
>> I don't need a test because I have a workaround: when I cast
>> FREE_NODE to its own type, i.e. (uint32_t const), then I don't
>> get an error anymore. That's simply a bug.

> You are changing it from lvalue to rvalue, which can make a lot of
> difference, for example when binding to a reference.

The value doesn't need to be defined according to what you quoted
from cppreference.com.

So when I write this in the constructor of my class ...
nd->pinCounter = (uint32_t const)FREE_NODE; // gcc-bug
... I get no linker-error.

When I write this ....
nd->pinCounter = FREE_NODE;
... I get the linker error because of a missing definition.

Paavo Helde

unread,
Oct 3, 2019, 3:35:04 PM10/3/19
to
On 3.10.2019 22:20, Bonita Montero wrote:
Also, if the compiler happens to compile some version of your code
without errors, this is no proof that your code is now correct.

Neither is this a proof that the compiler is buggy, because
(unfortunately) C++ compilers are not obliged to catch all errors in the
program.

Paavo Helde

unread,
Oct 3, 2019, 3:36:30 PM10/3/19
to
And the type of nd->pinCounter is ...?


Bonita Montero

unread,
Oct 3, 2019, 3:37:27 PM10/3/19
to
>> I don't need a test because I have a workaround: when I cast
>> FREE_NODE to its own type, i.e. (uint32_t const), then I don't
>> get an error anymore. That's simply a bug.

> Also, if the compiler happens to compile some version of your code
> without errors, this is no proof that your code is now correct.

You're so learning-resistant, that's unbelievable.
So read what yo quoted from cppreference!!!

> Neither is this a proof that the compiler is buggy, ...

There is definitely a bug since in my case there hasn't to be a
definition of the static const member. Read what you quted from
cppreference.

Bonita Montero

unread,
Oct 3, 2019, 3:38:42 PM10/3/19
to
>> When I write this ....
>>      nd->pinCounter = FREE_NODE;
>> ... I get the linker error because of a missing definition.

> And the type of nd->pinCounter is ...?

The same type, but that doesn't matter.

Bo Persson

unread,
Oct 3, 2019, 4:03:54 PM10/3/19
to
No, that is because the cast creates a new temporary with the same value.

An old trick has been to use f(+FREE_NODE); when passing as a parameter.
That passes a reference to the temporary and not a reference to the
constant.


Bo Persson

Bo Persson

unread,
Oct 3, 2019, 4:06:35 PM10/3/19
to
To clarify - the temporary here is the "return value" of unary operator+.


>    Bo Persson
>

Paavo Helde

unread,
Oct 3, 2019, 4:57:26 PM10/3/19
to
That's hard to believe. Maybe you have a bug elsewhere in your code.

Bonita Montero

unread,
Oct 3, 2019, 5:42:54 PM10/3/19
to
>> I don't need a test because I have a workaround: when I cast
>> FREE_NODE to its own type, i.e. (uint32_t const), then I don't
>> get an error anymore. That's simply a bug.
>
> No, that is because the cast creates a new temporary with the same value.

LOL.

James Kuyper

unread,
Oct 3, 2019, 10:20:38 PM10/3/19
to
On 3.10.2019 16:41, Bonita Montero wrote:
>> So does the following:
>>
>> struct A
>> {
>> struct B
>> {
>> int const ABC = 123;
>> int f();
>> };
>> };
>>
>> But those solutions are so obvious that I assume that they violate some
>> additional requirements that you failed to mention. If so, you need to
>> explain all the relevant requirements, so people will be better able to
>> help you.
>
> I need the constant in the outer class.
> With static I found a bug in the gcc, that I couln't use the constant in
> the constructor. The constants were defined this way:
>
> static
> std::uint32_t const UNPINNED_NODE = 0,
> FREE_NODE = 0xFFFFFFFFu;
>
>
> I assigned FREE_NODE to a member in this way.
>
> nd->pinCounter = (uint32_t const)FREE_NODE; // gcc-bug?
>
> Without the cast I got a linker-error (MSVC compiled the code).
> Surprisingly all other "accesses" to FREE_NODE in the outer class
> didn't give this linker-error. I don't know why gcc acesses FREE_NODE
> like it's not a const at this point in the constructor. So I asked for
> a work-around bcause I needed the constant in the innner class as well
> in the outer.

When reporting such a problem, you should
1. Present a complete program that is as simple as possible, while still
presenting the problem that you're talking about.
2. Identify the specific compiler that you used, including version
information, and the complete command line that you used to compile.
3. Show us the messages, if any, generated by compiling it.

I've constructed the simplest program I could come up with that was
consistent with everything you've said about this problem. It compiled,
linked, and executed without any problems. I used g++ (Ubuntu
7.4.0-1ubuntu1~18.04.1) 7.4.0. In order to give it an opportunity for a
link error, I broke it up into two source code files and a shared header
file:

A.h:
#define H_A
#include <cstdint>

struct A
{
static std::uint32_t const UNPINNED_NODE = 0,
FREE_NODE = 0xFFFFFFFFu;
struct B
{
int pinCounter;
int f();
} *nd;

A(){
nd = new B();
nd->pinCounter = FREE_NODE;
}

~A(){
delete nd;
}
};
#endif
===========================================================

A.cpp:
#include <iostream>
#include "A.h"

int main(void){
struct A a;
std::cout << a.nd->f() << std::endl;
}

===========================================================

abc.cpp:
#include <iostream>
#include "A.h"

int main(void){
struct A a;
std::cout << a.nd->f() << std::endl;
}

I compiled it using the following command line:

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 A.cpp abc.cpp -o abc

I executed it, producing the following results:

~/Programming/C++/testprog(67) abc
0
~/Programming/C++/testprog(68) echo $status
0

My program is consistent with everything you've said about the problem
you ran into. Therefore, the problem you ran into must be due to some
difference between your program and mine which you didn't tell us about.
Therefore, it would help if you could tell us about those differences.
Either provide a simplified version of your own program that
demonstrates the problem, or feel free to modify my program in whatever
way is needed to demonstrate it.

James Kuyper

unread,
Oct 3, 2019, 10:27:14 PM10/3/19
to
On 10/3/19 10:20 PM, James Kuyper wrote:

Sorry - I made a cut-and-paste error:

> A.cpp:
> #include <iostream>
> #include "A.h"
>
> int main(void){
> struct A a;
> std::cout << a.nd->f() << std::endl;
> }

That should have been:
#include "A.h"

int A::B::f(void){
return UNPINNED_NODE;
}

Cholo Lennon

unread,
Oct 3, 2019, 10:35:24 PM10/3/19
to
On 10/3/19 6:16 AM, Bonita Montero wrote:
>> There is no A object created anywhere, so there is no existing ABC
>> member to read and return, so the program cannot physically work. ...
>
> I know the reasons why this doesn't work.

So, if you know the reasons, why the first sentence in your first post
is "Why does this not work"?

> I only wanted to have a workaround without making ABC static.

--
Cholo Lennon
Bs.As.
ARG

Bonita Montero

unread,
Oct 3, 2019, 10:59:18 PM10/3/19
to
> When reporting such a problem, you should

I don't want to report this problem because I have a workaround.

> 3. Show us the messages, if any, generated by compiling it.

If I use the following code ..
nd->pinCounter = FREE_NODE; // gcc-bug
... I'll get the following linker-error ...
In function `LruCache<unsigned long, unsigned long, std::hash<unsigned
long>, std::equal_to<unsigned long>, std::allocator<char>
>::LruCache(unsigned long, unsigned long, unsigned long, unsigned long,
std::hash<unsigned long> const&, std::equal_to<unsigned long> const&,
std::allocator<char> const&)':
xxxxx.cpp:(.text._ZN8LruCacheImmSt4hashImESt8equal_toImESaIcEEC2EmmmmRKS1_RKS3_RKS4_[_ZN8LruCacheImmSt4hashImESt8equal_toImESaIcEEC5EmmmmRKS1_RKS3_RKS4_]+0x20d):
undefined reference to `LruCache<unsigned long, unsigned long,
std::hash<unsigned long>, std::equal_to<unsigned long>,
std::allocator<char> >::FREE_NODE'
collect2: error: ld returned 1 exit status
But only without optimzations.
But if I commet out this line, the linker-error vanishes - although I
have a lot of identical "accesses" to this static const "member" with
some other methods of this class.
If i cast it like this:
nd->pinCounter = (uint32_t const)FREE_NODE; // gcc-bug
The linker-message vanishes.

> I've constructed the simplest program I could come up with that was
> consistent with everything you've said about this problem. It compiled,
> linked, and executed without any problems. I used g++ (Ubuntu
> 7.4.0-1ubuntu1~18.04.1) 7.4.0. In order to give it an opportunity for a
> link error, I broke it up into two source code files and a shared header
> file:

I did the same and I didn't get any problems. And I don't wan't to strip
down my 970 lines class to the minimum where I still get this error.

Bonita Montero

unread,
Oct 3, 2019, 11:04:53 PM10/3/19
to
>>> But it's not really "odr-used" because a const-variable can be compile
>>> -time constant.

>> Irrelevant.

> No, not irrelevant. A definition isn't necessary here and what you
> quoted from cppreference.com exactly says that.

Cool, the whole issue is even mentioned in the Wikiedia:
https://en.wikipedia.org/wiki/One_Definition_Rule#Definitions_of_static_const_data_members

Bonita Montero

unread,
Oct 3, 2019, 11:18:23 PM10/3/19
to
>>> But it's not really "odr-used" because a const-variable can be compile
>>> -time constant.

>> Irrelevant.

> No, not irrelevant. A definition isn't necessary here and what you
> quoted from cppreference.com exactly says that.

The standard says the same:
"If a non-volatile non-inline const static data member is of integral
or enumeration type, its declaration in the class definition can specify
a brace-or-equal-initializer in which every initializer-clause that is
an assignment-expression is a constant expression (8.20). The member
shall still be defined in a namespace scope if it is odr-used (6.2) ..."

And there's an example of the odr-use of a static const member in
6.2 / 2.8 of the C++17 draft:
struct S { static const int x = 0; };
const int &f(const int &r);
int n = b ? (1, S::x) // S::x is not odr-used here
: f(S::x); // S::x is odr-used here, so a definition is required

Bonita Montero

unread,
Oct 3, 2019, 11:21:48 PM10/3/19
to
>>> But it's not really "odr-used" because a const-variable can be compile
>>> -time constant.

>> Irrelevant.

> No, not irrelevant. A definition isn't necessary here and what you
> quoted from cppreference.com exactly says that.

Keith Thompson

unread,
Oct 4, 2019, 12:29:27 AM10/4/19
to
Bonita Montero <Bonita....@gmail.com> writes:
>> When reporting such a problem, you should
>
> I don't want to report this problem because I have a workaround.

And you don't want anyone else to benefit from your discovery, or give
the gcc maintainers a chance to fix it?

[...]

Paavo Helde

unread,
Oct 4, 2019, 2:06:01 AM10/4/19
to
On 4.10.2019 5:59, Bonita Montero wrote:
>> When reporting such a problem, you should
>
> I don't want to report this problem because I have a workaround.
>
>> 3. Show us the messages, if any, generated by compiling it.
>
> If I use the following code ..
> nd->pinCounter = FREE_NODE; // gcc-bug
> ... I'll get the following linker-error ...
> In function `LruCache<unsigned long, unsigned long, std::hash<unsigned
> long>, std::equal_to<unsigned long>, std::allocator<char>
> >::LruCache(unsigned long, unsigned long, unsigned long, unsigned long,
> std::hash<unsigned long> const&, std::equal_to<unsigned long> const&,
> std::allocator<char> const&)':
> xxxxx.cpp:(.text._ZN8LruCacheImmSt4hashImESt8equal_toImESaIcEEC2EmmmmRKS1_RKS3_RKS4_[_ZN8LruCacheImmSt4hashImESt8equal_toImESaIcEEC5EmmmmRKS1_RKS3_RKS4_]+0x20d):
> undefined reference to `LruCache<unsigned long, unsigned long,
> std::hash<unsigned long>, std::equal_to<unsigned long>,
> std::allocator<char> >::FREE_NODE'
> collect2: error: ld returned 1 exit status

Ah! You failed to mention this is a template class. This is a totally
different can of worms!

Bonita Montero

unread,
Oct 4, 2019, 3:19:06 AM10/4/19
to
> And you don't want anyone else to benefit from your discovery, ...

I'm too lazy to strip down my class to the basic issue.

Bonita Montero

unread,
Oct 4, 2019, 3:20:31 AM10/4/19
to
>> undefined reference to `LruCache<unsigned long, unsigned long,
>> std::hash<unsigned long>, std::equal_to<unsigned long>,
>> std::allocator<char> >::FREE_NODE'
>> collect2: error: ld returned 1 exit status

> Ah! You failed to mention this is a template class.
> This is a totally different can of worms!

Explain me why this problem should be related to that it happens in a
tmeplated class ...

David Brown

unread,
Oct 4, 2019, 4:22:56 AM10/4/19
to
On 03/10/2019 17:32, Bonita Montero wrote:
>>> No, that's a clear sign that gcc has a bug at this point. At other
>>> placess FREE_NODE is compile-time-constant but in the constructor
>>> it is physically accessed.
>
>> No, the bug is in your code.
>
>> The separate definition is formally required (at least in older C++).
>
> No, the separate definition is not required if it can be evaluated to
> a compile-time constant. You can even define arrays with compile-time
> sizes on those const-members.
>

Yes, it is still needed - even if the compiler can handle it at compile
time, it is still a logical read.

If you use C++17, you can make it "constexpr", then you don't need an
extra definition for it.

Bonita Montero

unread,
Oct 4, 2019, 4:25:55 AM10/4/19
to
> Yes, it is still needed - even if the compiler can handle it at compile
> time, it is still a logical read.

No, not anymore since C++11.

James Kuyper

unread,
Oct 4, 2019, 8:43:46 AM10/4/19
to
On Thursday, October 3, 2019 at 10:59:18 PM UTC-4, Bonita Montero wrote:
> > When reporting such a problem, you should
>
> I don't want to report this problem because I have a workaround.

Keep in mind that you don't have an accurate description of the
situation that triggers the problem you ran into. Your description
includes programs that execute properly. As a result, you're likely to
end up using your work-around in situations where it isn't actually
needed. Efficiency is intelligent laziness - there's nothing efficient
about this laziness.

Since you haven't bothered tracking down what the actual problem is, you
can't be sure you don't also have the opposite problem with your
description - there might be programs you will someday write which will
fail for the same reason that this one failed, but which don't match
your description of the situation triggering your problem. And since you
don't know what the real problem is, you can't be sure that your work-
around will always successfully work around it.

From past experience, I don't expect you to pay any attention to this
advice. I'm just putting it out for the benefit of other readers.

Bonita Montero

unread,
Oct 4, 2019, 9:03:56 AM10/4/19
to
> Keep in mind that you don't have an accurate description of the
> situation that triggers the problem you ran into. Your description
> includes programs that execute properly. As a result, you're likely to
> end up using your work-around in situations where it isn't actually
> needed. ...

It is needed; without the cast gcc makes a memory-reference which it
isn't allowed to according to the standard. But according to the stan-
dard the compiler isn't allowed to make a memory-reference here (see
one definitoin rule exception for static const integral members of
a class, or read this [*]).

> Since you haven't bothered tracking down what the actual problem is,

I found that casting the value to its own type doesn't induce the
linker-error. So this is definitely compiler-bug. And other accesses
to this static constant in other methods don't induce this problem.

> And since you don't know what the real problem is, you can't be
> sure that your workround will always successfully work around it.

That's true for all compiler-features.
The fail with later versions of a compilwer.

[*]
https://en.wikipedia.org/wiki/One_Definition_Rule#Definitions_of_static_const_data_members

James Kuyper

unread,
Oct 4, 2019, 9:04:31 AM10/4/19
to
On 10/3/19 2:11 PM, Paavo Helde wrote:
> On 3.10.2019 20:53, Bonita Montero wrote:
>>
>>> Exactly the opposite, a definition is necessary because the language,
>>> ie. the C++ standard, requires it.
>>
>> No.
>>
>
> From https://en.cppreference.com/w/cpp/language/static :
>
> "If a const non-inline (since C++17) static data member or a constexpr
> static data member (since C++11) is odr-used, a definition at namespace
> scope is still required, but it cannot have an initializer. This
> definition is deprecated for constexpr data members (since C++17)."
>
> What's tricky is to see where in your program you have odr-use. For
> example, passing the data member to a function via a const reference
> qualifies as an odr-use. I guess that's what happened in your example.

The only use of the member that he's shown us, and the one that
apparently triggers his problem, is not an odr-use:

> static
> std::uint32_t const UNPINNED_NODE = 0,
> FREE_NODE = 0xFFFFFFFFu;
...
> When I write this ....
> nd->pinCounter = FREE_NODE;
> ... I get the linker error because of a missing definition.

"A variable x whose name appears as a potentially-evaluated expression
ex is odr-used by ex unless applying the lvalue-to-rvalue conversion
(7.1) to x yields a constant expression." (6.2p3).

The lvalue-to-rvalue conversion yields 0xFFFFFFFFu, which is a constant
expression, so this isn't an odr-use.

I'm confused by the "shall"s in the following two clauses:

"The definition for a static data member that is not defined inline in
the class definition shall appear in a namespace scope enclosing the
member’s class definition." (12.2.3.2p2)

"If a non-volatile non-inline const static data member is of integral or
enumeration type, its declaration in the class definition can specify a
brace-or-equal-initializer in which every initializer-clause that is an
assignment-expression is a constant expression (8.20). The member shall
still be defined in a namespace scope if it is odr-used (6.2) in the
program and the namespace scope definition shall not contain an
initializer." (12.2.3.2p3)

The "shall" in that second clause is conditional on whether the member
is odr-used. However, the "shall" from the previous paragraph inherently
applies to the members described in the second clause, and is
unconditional, so why is there any need to mention it again. And why
make the redundant shall conditional?

My best guess is that the conditional "shall" was meant to override the
unconditional one for the things to which it applies. In other words,
for a "... a non-volatile non-inline const static data member is of
integral or enumeration type, its declaration in the class definition
can specify a brace-or-equal-initializer in which every
initializer-clause that is an assignment-expression is a constant
expression", a namespace scope definition is not needed if the member is
not odr-used.

Which strikes me as a reasonable and convenient thing to allow.

James Kuyper

unread,
Oct 4, 2019, 9:20:25 AM10/4/19
to
On Friday, October 4, 2019 at 9:03:56 AM UTC-4, Bonita Montero wrote:
> > Keep in mind that you don't have an accurate description of the
> > situation that triggers the problem you ran into. Your description
> > includes programs that execute properly. As a result, you're likely to
> > end up using your work-around in situations where it isn't actually
> > needed. ...
>
> It is needed; without the cast gcc makes a memory-reference which it
> isn't allowed to according to the standard. But according to the stan-
> dard the compiler isn't allowed to make a memory-reference here (see
> one definitoin rule exception for static const integral members of
> a class, or read this [*]).
>
> > Since you haven't bothered tracking down what the actual problem is,
>
> I found that casting the value to its own type doesn't induce the
> linker-error. So this is definitely compiler-bug. And other accesses
> to this static constant in other methods don't induce this problem.

No, you haven't identified the real problem, just investigated it's
symptoms. If you've found a real gcc bug (since you won't show us the
other relevant code, I can't be sure whether it might be failing for
some other reason), only when a gcc developer figures out why gcc is
translating your code incorrectly will he know for certain which
situations will trigger the bug. And if this isn't a real bug - if some
other aspect of your code gives gcc permission to translate your code
this way, the you certainly don't know what that aspect is.

> > And since you don't know what the real problem is, you can't be
> > sure that your workround will always successfully work around it.
>
> That's true for all compiler-features.
> The fail with later versions of a compilwer.

Yes, but I'm not talking about later versions of the compiler. I'm
talking about other code written by you and compiled with the same
version of the compiler. Since you only know the symptoms, not the real
problem, it's quite possible that you'll see the same problem in other
situations that don't match your description of the situation that
triggers it.

Bonita Montero

unread,
Oct 4, 2019, 9:24:14 AM10/4/19
to
> No, you haven't identified the real problem, just investigated it's
> symptoms. ...

That's a matter of definition.

> Yes, but I'm not talking about later versions of the compiler. I'm
> talking about other code written by you and compiled with the same
> version of the compiler. Since you only know the symptoms, not the
> real problem, it's quite possible that you'll see the same problem
> in other situations that don't match your description of the situation
> that triggers it.

The problem is that the compiler odr-accesses a static const integral
member like an odr-access-member. If I see the same problem in other
situations it is the same.

Paavo Helde

unread,
Oct 4, 2019, 9:31:13 AM10/4/19
to
First, it is a bit more tricky to define a static data member of a class
template. Such a definition would still be a template, so it must go
into the header file together with the template, not into some random
translation unit where it might not be instantiated at all, not to speak
about that it cannot be instantiated for all potentially needed types.

Also, IIRC historically there have indeed been compiler bugs related to
template static data members, so there might even be a slight chance you
are indeed seeing a compiler bug. But as you refuse to prepare a
self-contained test this will remain just hypothetical.

Bonita Montero

unread,
Oct 4, 2019, 9:48:35 AM10/4/19
to
>> Explain me why this problem should be related to that it happens in a
>> tmeplated class ...

> First, it is a bit more tricky to define a static data member of a class
> template. Such a definition would still be a template, so it must go
> into the header file together with the template, not into some random
> translation unit where it might not be instantiated at all, not to speak
> about that it cannot be instantiated for all potentially needed types.

There isn't a need for a _definition_ if it isn't odr-used.

Paavo Helde

unread,
Oct 4, 2019, 12:29:49 PM10/4/19
to
The compiler disagreed with you. We don't know who is right.

Bonita Montero

unread,
Oct 4, 2019, 1:42:54 PM10/4/19
to
>> There isn't a need for a _definition_ if it isn't odr-used.

> The compiler disagreed with you. We don't know who is right.

I quoted the C++17 standard which says that a definition isn't necessary
when the member isn't odr-accessed and Wikipedia says that this is since
C++11.

Paavo Helde

unread,
Oct 4, 2019, 2:04:57 PM10/4/19
to
I should have expressed myself clearer. The compiler disagreed with you
about whether your code has odr-access or not. The fact that you got a
linker error shows that the compiler thought there was an odr-access to
the static member, and you claim there is not.


Bonita Montero

unread,
Oct 4, 2019, 2:10:56 PM10/4/19
to
>> I quoted the C++17 standard which says that a definition isn't necessary
>> when the member isn't odr-accessed and Wikipedia says that this is since
>> C++11.

> I should have expressed myself clearer. The compiler disagreed with you
> about whether your code has odr-access or not. The fact that you got a
> linker error shows that the compiler thought there was an odr-access to
> the static member, and you claim there is not.

Of coure there is not. The code-pattern is the same as given in the
standard. You have odr-access only if you take a reference or the
address of a static const integer member. And MSVC and clang handle
it correctly.

Paavo Helde

unread,
Oct 4, 2019, 2:24:54 PM10/4/19
to
On 4.10.2019 21:10, Bonita Montero wrote:
>>> I quoted the C++17 standard which says that a definition isn't necessary
>>> when the member isn't odr-accessed and Wikipedia says that this is since
>>> C++11.
>
>> I should have expressed myself clearer. The compiler disagreed with
>> you about whether your code has odr-access or not. The fact that you
>> got a linker error shows that the compiler thought there was an
>> odr-access to the static member, and you claim there is not.
>
> Of coure there is not.

We do not know that.

> And MSVC and clang handle it correctly.

If they compile the program with no diagnostics, it does not prove that
there is no odr-access. These compilers might just have optimized it
away, hiding the problem.

Bonita Montero

unread,
Oct 4, 2019, 2:38:17 PM10/4/19
to
>> And MSVC and clang handle it correctly.

> If they compile the program with no diagnostics, it does not prove that
> there is no odr-access. These compilers might just have optimized it
> away, hiding the problem.

There is no odr-access. And I have a identical assignments of FREE_NODE
as well as UNPINNED_NODE to nd->pinCounter in other methods that don't
fail although they do the same.

Paavo Helde

unread,
Oct 4, 2019, 2:58:32 PM10/4/19
to
That's a clear sign you don't understand why it was failing and how to
actually fix the code.

Bonita Montero

unread,
Oct 4, 2019, 3:06:18 PM10/4/19
to
>> There is no odr-access. And I have a identical assignments of FREE_NODE
>> as well as UNPINNED_NODE to nd->pinCounter in other methods that don't
>> fail although they do the same.

> That's a clear sign you don't understand why it was failing and how to
> actually fix the code.

No, surprisingly I haven't written the g++-frontend.
But it should do the same with identical code.
I do ...
nd->pinCounter = FREE_NODE;
... multiple times in multiple functions.
nd has always the same type and only in one case this fails.

David Brown

unread,
Oct 5, 2019, 5:52:22 AM10/5/19
to
Did you specify C++17 standard when you compiled? IIRC, gcc defaults to
C++14 (or, more accurately, g++14) these days. It is entirely possible,
likely even, that when using C++17 there are extra assembly declarations
generated to make it work without an extra definition.

Bonita Montero

unread,
Oct 5, 2019, 6:41:29 AM10/5/19
to
>> I quoted the C++17 standard which says that a definition isn't necessary
>> when the member isn't odr-accessed and Wikipedia says that this is since
>> C++11.

> Did you specify C++17 standard when you compiled?  IIRC, gcc
> defaults to C++14 (or, more accurately, g++14) these days.

The above rule is since C++11.

Melzzzzz

unread,
Oct 5, 2019, 7:04:53 AM10/5/19
to
This is pretty impossible if you generate object file. This is only
possible if you make executable from all source files.
>


--
press any key to continue or any other to quit...
U ničemu ja ne uživam kao u svom statusu INVALIDA -- Zli Zec
Na divljem zapadu i nije bilo tako puno nasilja, upravo zato jer su svi
bili naoruzani. -- Mladen Gogala

David Brown

unread,
Oct 5, 2019, 7:09:15 AM10/5/19
to
C++17 has changed some of the details here. Since you won't show the
code, and don't give details of the builds, it's impossible to guess
where your problem lies. One possibility is that your code is affected
by these details, and you are using different standards on the different
compilers.

<https://en.cppreference.com/w/cpp/language/static>

The other likely possibility is that your code has an error, but some
compilers (or linkers) are more forgiving of that error than others.

The possibility that you are right and gcc has a bug exists, but is
/way/ down on the list of likelihoods. If you post a small, compilable
snippet demonstrating the issue, then we can figure it out. If gcc has
a bug, then we will all learn something, and an issue can be filed so
that gcc gets fixed. If the bug is in your code or your understanding,
then /you/ will learn something - as might others following the thread.
But if you continue to hide the problem, we have no choice but to assume
you are wrong and you don't want to be corrected.



Bonita Montero

unread,
Oct 5, 2019, 7:21:54 AM10/5/19
to
>> The above rule is since C++11.

> C++17 has changed some of the details here.

No, I've quoted from the C++17 draft-standard. It says the same
cppreference and the Wikipedia says. And the example-code is also
the samle like on cppreference.

Bonita Montero

unread,
Oct 5, 2019, 7:23:23 AM10/5/19
to
>> I quoted the C++17 standard which says that a definition isn't necessary
>> when the member isn't odr-accessed and Wikipedia says that this is since
>> C++11.

> This is pretty impossible if you generate object file. This is only
> possible if you make executable from all source files.

Boy, you're so stupid.

Melzzzzz

unread,
Oct 5, 2019, 7:32:32 AM10/5/19
to
If I am stupid, you are batshit stupid...

Bo Persson

unread,
Oct 5, 2019, 7:53:32 AM10/5/19
to
If you *are* coding for C++17, you can use constexpr for the constants
and avoid the problem entirely.

If you don't use C+17, it doesn't matter what the newest standard says.


Bo Perssob

Bonita Montero

unread,
Oct 5, 2019, 10:35:55 AM10/5/19
to
>> No, I've quoted from the C++17 draft-standard. It says the same
>> cppreference and the Wikipedia says. And the example-code is also
>> the samle like on cppreference.

> If you *are* coding for C++17, you can use constexpr for the constants
> and avoid the problem entirely.

I use static const.

> If you don't use C+17, it doesn't matter what the newest standard says.

Google, and you find out that this is identical with C++17 an C++11.

Öö Tiib

unread,
Oct 7, 2019, 3:15:00 PM10/7/19
to
On Saturday, 5 October 2019 14:09:15 UTC+3, David Brown wrote:
> If the bug is in your code or your understanding,
> then /you/ will learn something - as might others following the thread.

The OP question was about why such novice coding defect does not
compile. Answers got usual snarky, boring and boneheaded replies.
Who has patience to follow such threads? There are no gems in
that trash.

James Kuyper

unread,
Oct 8, 2019, 10:39:02 AM10/8/19
to
On Friday, October 4, 2019 at 9:24:14 AM UTC-4, Bonita Montero wrote:
> > No, you haven't identified the real problem, just investigated it's
> > symptoms. ...
>
> That's a matter of definition.

Perhaps - but if it's a defect in the gcc compiler code, you can't tell
me what the defect in that code is, or where to find it. If it's a
defect in some other part of your own code that gives gcc permission to
behave this way, you don't know what that other part of your code is.
Either way, there's a gap in your knowledge related to this problem, and
you can't be sure what's the correct way to fix it until you've filled
that gap. It really doesn't matter what terms you use to describe that
gap.

I'm not saying that you're responsible for searching the gcc code for
the defect; you should rather file a defect report, and let gcc
developers investigate. However, if you were bothering to file such a
report, it should be accompanied by more information about the code that
triggers it than you've provided to us so far.

> > Yes, but I'm not talking about later versions of the compiler. I'm
> > talking about other code written by you and compiled with the same
> > version of the compiler. Since you only know the symptoms, not the
> > real problem, it's quite possible that you'll see the same problem
> > in other situations that don't match your description of the situation
> > that triggers it.
>
> The problem is that the compiler odr-accesses a static const integral
> member like an odr-access-member. ...

That's just a symptom (combined with some guesses about how the symptom
is occurring). If it's a bug in the compiler, you don't know what
triggers that bug. If it's a defect in some other part of your code (the
part you refuse to show) which gives permission for gcc to behave this
way, you don't know what that defect is.

> ... If I see the same problem in other
> situations it is the same.

It's the same problem if it has the same cause, whether that cause is a
defect in the compiler or in your own code. Either way, until you know
what the defect is, you can't guarantee that the next time the defect
causes trouble, it will show the same symptoms; there's a decent chance
that it won't.

Bonita Montero

unread,
Oct 8, 2019, 10:46:54 AM10/8/19
to
> That's just a symptom (combined with some guesses about how the symptom
> is occurring). If it's a bug in the compiler, you don't know what
> triggers that bug. If it's a defect in some other part of your code (the
> part you refuse to show) which gives permission for gcc to behave this
> way, you don't know what that defect is.


Why should other code trigger that behaviour legally?

James Kuyper

unread,
Oct 8, 2019, 11:45:52 AM10/8/19
to
Code anywhere else in your program that has undefined behavior can
legally change the behavior of any other part of your code, including
this one. This is not some pedantic, purely theoretical issue - in real
life the way code with undefined behavior actually behaves can be quite
odd and counter-intuitive; "common sense" is not necessarily a useful
guide to what's actually possible.

Unless and until you show us a complete program, preferably as simple as
possible, which demonstrates the problem you're having, we can't rule
out the possibility that the actual problem lies somewhere else in your
code. You can rule it out if you want to, and apparently have already
done so, but I doubt that your decision to do so was well-justified.

Bonita Montero

unread,
Oct 8, 2019, 11:56:49 AM10/8/19
to
>> Why should other code trigger that behaviour legally?

> Code anywhere else in your program that has undefined behavior can
> legally change the behavior of any other part of your code, ...

No, not in this case. nd has always the same type and pinCounter is
a member which is uint32_t. And FREE_NODE is a static const uint32_t
member. So the code-pattern is always the same in different functions
(one constructor and several methods) and can be considered isolated
from everything else.

Ned Latham

unread,
Oct 8, 2019, 12:27:04 PM10/8/19
to
James Kuyper wrote:
> Bonita Montero wrote:
> > >
> > > That's just a symptom (combined with some guesses about how the symptom
> > > is occurring). If it's a bug in the compiler, you don't know what
> > > triggers that bug. If it's a defect in some other part of your code (the
> > > part you refuse to show) which gives permission for gcc to behave this
> > > way, you don't know what that defect is.
> >
> > Why should other code trigger that behaviour legally?

I'm new here: missed the beginning of the thread, sorry.

> Code anywhere else in your program that has undefined behavior can
> legally change the behavior of any other part of your code, including
> this one.

I'm completeky lost here: how can source code affect gcc?

> This is not some pedantic, purely theoretical issue - in real
> life the way code with undefined behavior actually behaves can be quite
> odd and counter-intuitive; "common sense" is not necessarily a useful
> guide to what's actually possible.

Just what does that term "code with undefined behaviour" mean here? Do you
mean function pointers (surely you're not talking about self-modifying code)?

> Unless and until you show us a complete program, preferably as simple as
> possible, which demonstrates the problem you're having, we can't rule
> out the possibility that the actual problem lies somewhere else in your
> code. You can rule it out if you want to, and apparently have already
> done so, but I doubt that your decision to do so was well-justified.

If you're talking about function pointers, it's not difficult to rule out
problems in source code. It's doable in self-modifying code too, for that
matter.

James Kuyper

unread,
Oct 8, 2019, 12:27:58 PM10/8/19
to
Feel free to consider it isolated if you wish. However, code with
precisely those characteristics can (and often does) have it's behavior
affected by code elsewhere in the same program that gives the entire
program undefined behavior.

I can't even begin to imagine what might have caused the symptoms you
reported - there's way too many different possibilities. But if you
provided a complete compilable program that produces them, I could start
analysing it to see if it contains any such defects. And if it doesn't,
and you provided the gcc team with a bug report containing such a
program, they could start tracking down why gcc handles it incorrectly.

All that you achieve by relying upon your work-around is an unjustified
sense of safety. But I suppose that sometimes if feels good just to
think that you're safe, even if you actually aren't.

Bonita Montero

unread,
Oct 8, 2019, 12:32:31 PM10/8/19
to
> Feel free to consider it isolated if you wish. However, code with
> precisely those characteristics can (and often does) have it's behavior
> affected by code elsewhere in the same program that gives the entire
> program undefined behavior.

No, "nd->pinCounter = FREE_NODE;" is semantically the same at any method
or constructor.

James Kuyper

unread,
Oct 8, 2019, 12:48:30 PM10/8/19
to
On 10/8/19 12:26 PM, Ned Latham wrote:
> James Kuyper wrote:
...
>> Code anywhere else in your program that has undefined behavior can
>> legally change the behavior of any other part of your code, including
>> this one.
>
> I'm completeky lost here: how can source code affect gcc?

gcc reads the source code, translates it in accordance with the
standard, and generates a program that meets the requirements that the
standard imposes on the results of translating such code. The standard
doesn't impose any requirements on the results of translating code that
has undefined behavior - so the fact that gcc's output meets those
non-existent requirements shouldn't give you the slightest feeling of
safety. None. Whatsoever.

>> This is not some pedantic, purely theoretical issue - in real
>> life the way code with undefined behavior actually behaves can be quite
>> odd and counter-intuitive; "common sense" is not necessarily a useful
>> guide to what's actually possible.
>
> Just what does that term "code with undefined behaviour" mean here?

"undefined behavior" is defined by the standard as identifying "behavior
for which this International Standard imposes no requirements" (3.27).
You actually have to read the standard to locate all of the places where
it says that the behavior is undefined, in order to determine what kinds
of code fall into that category. By my count, the latest draft of the
standard that I have access to, N4659.pdf, contains 68 occurrences of
that term. In itself, that's not quite as bad as it sounds - not all of
those occurrences represent distinct reasons for code to have undefined
behavior (though most of them do).
However, the C++ standard cross-references the C standard for virtually
the entire C standard library, so every occurrence of "undefined
behavior" in the C standard that applies to it's library might also
apply to C++. Also, keep in mind that the behavior of many parts of the
C++ standard library are defined in terms of the behavior of the C
standard library (for instance, <iostream> heavily cross-references
<stdio.h>). As a result, code which makes no direct use of the C
standard library might have undefined behavior for reasons that you
would have to look at the C standard to understand.


Do you
> mean function pointers (surely you're not talking about self-modifying code)?

There's many ways that function pointers can be involved when code has
undefined behavior. However, there's also many other ways the behavior
can be undefined.

>> Unless and until you show us a complete program, preferably as simple as
>> possible, which demonstrates the problem you're having, we can't rule
>> out the possibility that the actual problem lies somewhere else in your
>> code. You can rule it out if you want to, and apparently have already
>> done so, but I doubt that your decision to do so was well-justified.
>
> If you're talking about function pointers,

I wasn't.

> ... it's not difficult to rule out
> problems in source code. It's doable in self-modifying code too, for that
> matter.

Ruling out the existence of any problems in any significant body of code
is essentially impossible. Detecting problems is often easy, but being
certain that you've found and corrected all such problems is always the
result of self-delusion. You might happen to be right that there are no
remaining defects, but certainty about that fact can't be justified.
Such certainty is, however, a very popular delusion.

James Kuyper

unread,
Oct 8, 2019, 3:17:42 PM10/8/19
to
No, if code elsewhere in the program renders the behavior of the entire
program undefined, then valid semantics for one occurrence of
nd->pincounter=FREE_NODE could include whistling Dixie, while a
different occurrence could have the semantics of counting down to a
blastoff.

You really don't appreciate how thoroughgoing the meaning of "undefined behavior" is. Pay attention to the key phrase: "no requirements". None
at all. In particular, there's no requirement that the specified piece
of memory to modified to hold the specified value. You cannot break a C
program into separate pieces and say: no error in piece A can affect
the behavior of the code in piece B. The language isn't defined in such
a compartmentalized fashion.

And that's not just theoretical - undefined behavior in other parts of
the code could allow optimizations to this code, which result in it not
being executed at all, or being executed out of sequence. It could also
cause the current instruction register to jump to just about any
location in program memory, and have whatever is in that location to
start being executed.

That's why it's essential, whenever one part of your program behaves
oddly (particularly if it behaves very oddly), to consider the
possibility that the behavior of the entire program is undefined due to
a defect in some other part of the program. The simplest way to deal
with that possibility is to remove all of the other parts of the program.
If you still see the odd behavior, then it is caused only by something
you didn't remove (but not necessarily by the thing that you think it
might be caused by). If the behavior ceases being odd, then the odd
behavior depends, at least in part, on something that you removed.

Ned Latham

unread,
Oct 8, 2019, 8:36:03 PM10/8/19
to
James Kuyper wrote:
> Ned Latham wrote:
> > James Kuyper wrote:
> ...
> > > Code anywhere else in your program that has undefined behavior can
> > > legally change the behavior of any other part of your code, including
> > > this one.
> >
> > I'm completeky lost here: how can source code affect gcc?
>
> gcc reads the source code, translates it in accordance with the
> standard, and generates a program that meets the requirements that the
> standard imposes on the results of translating such code. The standard
> doesn't impose any requirements on the results of translating code that
> has undefined behavior - so the fact that gcc's output meets those
> non-existent requirements shouldn't give you the slightest feeling of
> safety. None. Whatsoever.

I have some searching to do. Thanks a lot.

> > > This is not some pedantic, purely theoretical issue - in real
> > > life the way code with undefined behavior actually behaves can be quite
> > > odd and counter-intuitive; "common sense" is not necessarily a useful
> > > guide to what's actually possible.
> >
> > Just what does that term "code with undefined behaviour" mean here?
>
> "undefined behavior" is defined by the standard as identifying "behavior
> for which this International Standard imposes no requirements" (3.27).
> You actually have to read the standard to locate all of the places where
> it says that the behavior is undefined, in order to determine what kinds
> of code fall into that category. By my count, the latest draft of the
> standard that I have access to, N4659.pdf, contains 68 occurrences of
> that term.

I now have a copy, thanks. I was going to print it, but I think I'll
rely on reading it onscreen.

> In itself, that's not quite as bad as it sounds - not all of
> those occurrences represent distinct reasons for code to have undefined
> behavior (though most of them do).

One's easy to guess: what happens when terminator-dependent string functions
are run over character arrays?

> However, the C++ standard cross-references the C standard for virtually
> the entire C standard library, so every occurrence of "undefined
> behavior" in the C standard that applies to it's library might also
> apply to C++. Also, keep in mind that the behavior of many parts of the
> C++ standard library are defined in terms of the behavior of the C
> standard library (for instance, <iostream> heavily cross-references
> <stdio.h>).

Hmm. I avoid almost all of that code, choosing instead low-level file
handling and raw character-level I/O using only low-level routines in
ctype.h, stdio.h and stdlib.h.

> As a result, code which makes no direct use of the C
> standard library might have undefined behavior for reasons that you
> would have to look at the C standard to understand.

As with there being no means in stdio.h for testing file status? I
see no way to avoid that problem without portability problems.

> > Do you mean function
> > pointers (surely you're not talking about self-modifying code)?
>
> There's many ways that function pointers can be involved when code has
> undefined behavior. However, there's also many other ways the behavior
> can be undefined.

Got it.

> > > Unless and until you show us a complete program, preferably as simple as
> > > possible, which demonstrates the problem you're having, we can't rule
> > > out the possibility that the actual problem lies somewhere else in your
> > > code. You can rule it out if you want to, and apparently have already
> > > done so, but I doubt that your decision to do so was well-justified.
> >
> > If you're talking about function pointers,
>
> I wasn't.
>
> > ... it's not difficult to rule out
> > problems in source code. It's doable in self-modifying code too,
> > for that matter.
>
> Ruling out the existence of any problems in any significant body of code
> is essentially impossible.

I see "significant" here as subject to opinion.

> Detecting problems is often easy, but being
> certain that you've found and corrected all such problems is always the
> result of self-delusion.

Depends on how you treat error conditions. I eschew raising exceptions
(I eschew <iostream> because it raises them): instead I use a my own
little library, which includes a class that does all I/O with just two
functions, Read() and Write(). (All file operations are done by the caller
so that the portability bug is removed from the library.) Possible errors
in those two methods are few, easy to se, and usually easy to deal with.
When an error is detected the class raises an error condition *in the
object*, disaqbling it and uniquely describing the error. That's all it
does, except that the total number of possible error conditions is 100:
it includes flow-on error conditions, making a nice trail to follow.

The result is that callers can deal with errors intelligently: they can
begin development querying the status of objects after every operation
and cut back as regions of code become robust.

> You might happen to be right that there are no
> remaining defects, but certainty about that fact can't be justified.

It can with encapsulation and if all the possibilities for error within
a scope are examined.

> Such certainty is, however, a very popular delusion.

If it's a delusion, I share it.

James Kuyper

unread,
Oct 8, 2019, 9:22:13 PM10/8/19
to
On 10/8/19 8:35 PM, Ned Latham wrote:
> James Kuyper wrote:
...
>> In itself, that's not quite as bad as it sounds - not all of
>> those occurrences represent distinct reasons for code to have undefined
>> behavior (though most of them do).
>
> One's easy to guess: what happens when terminator-dependent string functions
> are run over character arrays?

"A string is a contiguous sequence of characters terminated by and
including the first null character." (7.1.1p1). The term "string" is
italicized, an ISO convention indicating that this sentence constitutes
the official definition of that term. As a result, if the description of
a standard library function describes one of the arguments of that
function as pointing at the first character of a string, the behavior is
undefined if the corresponding argument points at memory that is not
null-terminated. Typically, the actual behavior will involve continuing
to search for a null character beyond the end of that object, a search
whose results are unpredictable, and likely to result in memory access
violations if the search goes on long enough.

>> However, the C++ standard cross-references the C standard for virtually
>> the entire C standard library, so every occurrence of "undefined
>> behavior" in the C standard that applies to it's library might also
>> apply to C++. Also, keep in mind that the behavior of many parts of the
>> C++ standard library are defined in terms of the behavior of the C
>> standard library (for instance, <iostream> heavily cross-references
>> <stdio.h>).
>
> Hmm. I avoid almost all of that code, choosing instead low-level file
> handling and raw character-level I/O using only low-level routines in
> ctype.h, stdio.h and stdlib.h.

ctype.h: "In all cases the argument is an int, the value of which shall
be representable as an unsigned char or shall equal the value of the
macro EOF. If the argument has any other value, the behavior is undefined."

stdio.h: 21 occurrences of "the behavior is undefined". 55 occurrences
of "shall", most of which impose requirements on programs which, if
violated, render the behavior undefined.

stdlib.h: 12 occurrences of "the behavior is undefined". 30 occurrences
of "shall".

...
>> Ruling out the existence of any problems in any significant body of code
>> is essentially impossible.
>
> I see "significant" here as subject to opinion.

Agreed.

Ned Latham

unread,
Oct 8, 2019, 9:39:06 PM10/8/19
to
James Kuyper wrote:

That was quick, James. Thanks.

> Ned Latham wrote:
> > James Kuyper wrote:
> ...
> > > In itself, that's not quite as bad as it sounds - not all of
> > > those occurrences represent distinct reasons for code to have undefined
> > > behavior (though most of them do).
> >
> > One's easy to guess: what happens when terminator-dependent string functions
> > are run over character arrays?
>
> "A string is a contiguous sequence of characters terminated by and
> including the first null character." (7.1.1p1). The term "string" is
> italicized, an ISO convention indicating that this sentence constitutes
> the official definition of that term. As a result, if the description of
> a standard library function describes one of the arguments of that
> function as pointing at the first character of a string, the behavior is
> undefined if the corresponding argument points at memory that is not
> null-terminated. Typically, the actual behavior will involve continuing
> to search for a null character beyond the end of that object, a search
> whose results are unpredictable, and likely to result in memory access
> violations if the search goes on long enough.

Which it can very easily. It's a C/C++ language bug, imo, and a pernicious
one at that: there's no way to avoid string constants, and if someone
misuses a character array as such in code you call, you'd better know the
details or a segfault is pretty much inevitable, sooner or later.

> > > However, the C++ standard cross-references the C standard for virtually
> > > the entire C standard library, so every occurrence of "undefined
> > > behavior" in the C standard that applies to it's library might also
> > > apply to C++. Also, keep in mind that the behavior of many parts of the
> > > C++ standard library are defined in terms of the behavior of the C
> > > standard library (for instance, <iostream> heavily cross-references
> > > <stdio.h>).
> >
> > Hmm. I avoid almost all of that code, choosing instead low-level file
> > handling and raw character-level I/O using only low-level routines in
> > ctype.h, stdio.h and stdlib.h.
>
> ctype.h: "In all cases the argument is an int, the value of which shall
> be representable as an unsigned char or shall equal the value of the
> macro EOF. If the argument has any other value, the behavior is undefined."

Mine are all [0..255], unsigned char.

> stdio.h: 21 occurrences of "the behavior is undefined". 55 occurrences
> of "shall", most of which impose requirements on programs which, if
> violated, render the behavior undefined.

Are these quotes from the draft (I haven't done any work yet)?

> stdlib.h: 12 occurrences of "the behavior is undefined". 30 occurrences
> of "shall".

Oof. Thanks again.

----snip----

James Kuyper

unread,
Oct 8, 2019, 10:15:52 PM10/8/19
to
On 10/8/19 9:38 PM, Ned Latham wrote:
> James Kuyper wrote:
>
> That was quick, James. Thanks.
>
>> Ned Latham wrote:
>>> James Kuyper wrote:
...
>>>> However, the C++ standard cross-references the C standard for virtually
>>>> the entire C standard library, so every occurrence of "undefined
>>>> behavior" in the C standard that applies to it's library might also
>>>> apply to C++. Also, keep in mind that the behavior of many parts of the
>>>> C++ standard library are defined in terms of the behavior of the C
>>>> standard library (for instance, <iostream> heavily cross-references
>>>> <stdio.h>).
>>>
>>> Hmm. I avoid almost all of that code, choosing instead low-level file
>>> handling and raw character-level I/O using only low-level routines in
>>> ctype.h, stdio.h and stdlib.h.
>>
>> ctype.h: "In all cases the argument is an int, the value of which shall
>> be representable as an unsigned char or shall equal the value of the
>> macro EOF. If the argument has any other value, the behavior is undefined."
>
> Mine are all [0..255], unsigned char.

That makes it easy - but many people write code that assumes that "plain
char" is unsigned, and which can therefore break if ported to a platform
where plain char is signed. What makes this problem harder to notice is
the fact that all members of the basic execution character set are
required to be positive, even if char is signed. People who don't need
to use any members of the extended character set (which includes many
people who produce software exclusively for the US market), are likely
never to trigger the defects in such code.

>> stdio.h: 21 occurrences of "the behavior is undefined". 55 occurrences
>> of "shall", most of which impose requirements on programs which, if
>> violated, render the behavior undefined.
>
> Are these quotes from the draft (I haven't done any work yet)?

Yes, but since the C++ standard incorporates the C standard library by
reference (with minor modifications), the relevant draft is a different
one from the one I cited before. I should have mentioned that fact.
Specifically, those counts are from n1570.pdf, which differs in only
minor ways from the final version of C2011. Note: I'm not claiming to
have done a perfectly accurate count - my point is merely that there's a
lot of those things.

Bonita Montero

unread,
Oct 8, 2019, 11:45:27 PM10/8/19
to
> You really don't appreciate how thoroughgoing the meaning of "undefined
> behavior" is.

Are you so stupid like Melzzzzz? I'm talking about compile-time
semantics and not behaviour at runtime. And it is the same at all
points where I wrote "nd->pinCounter = FREE_NODE;".

Paavo Helde

unread,
Oct 9, 2019, 1:45:51 AM10/9/19
to
On 9.10.2019 4:38, Ned Latham wrote:
> James Kuyper wrote:
>> "A string is a contiguous sequence of characters terminated by and
>> including the first null character." (7.1.1p1). The term "string" is
>> italicized, an ISO convention indicating that this sentence constitutes
>> the official definition of that term. As a result, if the description of
>> a standard library function describes one of the arguments of that
>> function as pointing at the first character of a string, the behavior is
>> undefined if the corresponding argument points at memory that is not
>> null-terminated. Typically, the actual behavior will involve continuing
>> to search for a null character beyond the end of that object, a search
>> whose results are unpredictable, and likely to result in memory access
>> violations if the search goes on long enough.
>
> Which it can very easily. It's a C/C++ language bug, imo, and a pernicious
> one at that: there's no way to avoid string constants, and if someone
> misuses a character array as such in code you call, you'd better know the
> details or a segfault is pretty much inevitable, sooner or later.

This is one more reason to avoid C-style strings, and functions working
with C-style strings, in C++.

FYI: there are now convenient ways to write std::string literal
constants directly in the code, and there is also std::string_view which
can be used to encapsulate any raw character arrays as proper C++ strings.

Ned Latham

unread,
Oct 9, 2019, 2:42:59 AM10/9/19
to
Paavo Helde wrote:
> Ned Latham wrote:
> > James Kuyper wrote:
> > >
> > > "A string is a contiguous sequence of characters terminated by and
> > > including the first null character." (7.1.1p1). The term "string" is
> > > italicized, an ISO convention indicating that this sentence constitutes
> > > the official definition of that term. As a result, if the description of
> > > a standard library function describes one of the arguments of that
> > > function as pointing at the first character of a string, the behavior is
> > > undefined if the corresponding argument points at memory that is not
> > > null-terminated. Typically, the actual behavior will involve continuing
> > > to search for a null character beyond the end of that object, a search
> > > whose results are unpredictable, and likely to result in memory access
> > > violations if the search goes on long enough.
> >
> > Which it can very easily. It's a C/C++ language bug, imo, and a pernicious
> > one at that: there's no way to avoid string constants, and if someone
> > misuses a character array as such in code you call, you'd better know the
> > details or a segfault is pretty much inevitable, sooner or later.
>
> This is one more reason to avoid C-style strings, and functions working
> with C-style strings, in C++.

Which is what I do.

> FYI: there are now convenient ways to write std::string literal

I have my own C++ standard library, preferable by far to the one James
mentioned above.

> constants directly in the code, and there is also std::string_view which
> can be used to encapsulate any raw character arrays as proper C++ strings.

My string class does it with a constructor (unsigned char*,unsigned int).
It is loading more messages.
0 new messages