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

Can similar programs be legel, warning or error?

84 views
Skip to first unread message

wyn...@gmail.com

unread,
Aug 20, 2020, 10:24:42 AM8/20/20
to
For this simplifed test.cpp, all are fine. But in another roughly identical
(a little bit more members) case, g++ says:
"warning: returning reference to temporary"
"error: cannot bind non-const lvalue reference of type."
So, my question: Is such way of coding 1.legal 2.returning reference to
temporary and/or cannot bind non-const lvalue reference of type, which or
both are correct!

//-----------------------------------
// test.cpp
#include <iostream>

using namespace std;

class Arr {
typedef uint16_t T;

T arr[2];

public:
inline const T& lo() const { return arr[0]; };
inline T& lo() { return arr[0]; };
inline const T& hi() const { return arr[1]; };
inline T& hi() { return arr[1]; };
};

int main()
{
Arr a;
a.lo()=1234;
a.hi()=0;
cout << "a.lo()=" << a.lo() << ", a.hi()=" << a.hi() << endl;
return 0;
};

//------------------------------------
[]$ g++ test.cpp
[]$ ./a.out
a.lo()=1234, a.hi()=0

James Kuyper

unread,
Aug 20, 2020, 11:21:27 AM8/20/20
to
On 8/20/20 10:24 AM, wyn...@gmail.com wrote:
> For this simplifed test.cpp, all are fine. But in another roughly identical
> (a little bit more members) case, g++ says:
> "warning: returning reference to temporary"
> "error: cannot bind non-const lvalue reference of type."

Yes, it's quite common to have two nearly identical programs, one of
them without any problems, and the other with undefined behavior, even
though the difference between them is very small. This is a most common
when the author is unaware of the fact that the difference, while small,
is important.
That is why it was a very bad idea to post only the program that doesn't
trigger any warning and error messages.

wyn...@gmail.com

unread,
Aug 20, 2020, 12:06:52 PM8/20/20
to
James Kuyper於 2020年8月20日星期四 UTC+8下午11時21分27秒寫道:
[quote]
So, my question: Is such way of coding 1.legal 2.returning reference to
temporary and/or cannot bind non-const lvalue reference of type, which or
both are correct!
----
I mean the very test.cpp as shown. Even with -O2 or -Os compile option are good.

wyn...@gmail.com

unread,
Aug 20, 2020, 12:16:58 PM8/20/20
to
wyn...@gmail.com於 2020年8月21日星期五 UTC+8上午12時06分52秒寫道:
It looked to me compiler can choose at will either 1.legal, accepted, or
2."warning: returning reference to temporary" and/or "error: cannot bind non-
const lvalue reference of type,..", rejected.

Bo Persson

unread,
Aug 20, 2020, 1:08:25 PM8/20/20
to
No, the compiler doesn't make a random choice. :-)

However, the code sample you posted only has references to lvalues (Arr
a; is an lvalue, and so are its members). There are no temporaries involved.

Of course there are many cases where a conversion would occur, like when
not everything involved uses type T. But that would then be in the code
we haven't seen.


Bo Persson

James Kuyper

unread,
Aug 20, 2020, 1:20:19 PM8/20/20
to
On 8/20/20 12:16 PM, wyn...@gmail.com wrote:
...
> It looked to me compiler can choose at will either 1.legal, accepted, or
> 2."warning: returning reference to temporary" and/or "error: cannot bind non-
> const lvalue reference of type,..", rejected.

An implementation is permitted to complain about anything it wants to.
It can complain about the fact that you used taboo words as identifiers.
It can complain about the fact that you compiled your code on Friday the
13th.
However, a conforming implementation of C++ needs an actual legitimate
reason for refusing to translate your code. So it would help a great
deal if you could show us the code that gave the compiler you're using
justification for doing so. The code you've showed us doesn't.

wyn...@gmail.com

unread,
Aug 20, 2020, 3:09:56 PM8/20/20
to
Bo Persson於 2020年8月21日星期五 UTC+8上午1時08分25秒寫道:
I am implementing a double-sized integer. This is the closest simplified version
All the relevant members I can think of are shown in DxInt, g++ does not
complain others function members. Stll can't replicate the error.

test2.cpp may be correct. but i need time to verify this, meanwhile, may you
people provides improvements of such implement, thanks.

//-------------------------------------------------
// test2.cpp
#include <iostream>

using namespace std;

template<typename T> class DxInt;

typedef uint32_t T;
typedef uint64_t TT;
template<> class DxInt<T> {

T arr[2];

public:
DxInt() { *this=0; };
DxInt(const DxInt& s) { *this=s; };
DxInt(TT v) { *this=v; };

inline const T& lo() const { return arr[0]; };
inline T& lo() { return arr[0]; };
inline const T& hi() const { return arr[1]; };
inline T& hi() { return arr[1]; };

inline DxInt& operator=(TT rhs) {
//*this=rhs; // gdb halts at this line. seems a recusive call
// nothing to do with "cannot bind non-const lvalue
// reference of type" or about "returning reference to
// temporary"
static_cast<TT&>(*this)=rhs;
return *this;
};

operator TT& () { return reinterpret_cast<TT&>(*this); };
operator const TT& () const { return reinterpret_cast<const TT&>(*this); };
};

int main()
{
DxInt<T> a;
a.lo()=1234;
a.hi()=9;
a=2+a;
--a;
cout << "a.lo()=" << a.lo() << ", a.hi()=" << a.hi() << endl;

DxInt<T> b(a);
cout << "b.lo()=" << b.lo() << ", b.hi()=" << b.hi() << endl;

const DxInt<T> c(100);
cout << "c.lo()=" << c.lo() << ", c.hi()=" << c.hi() << endl;

return 0;
};

//-----------------

[]$ g++ test2.cpp -O2
[]$ ./a.out
a.lo()=1235, a.hi()=9
b.lo()=1235, b.hi()=9
c.lo()=100, c.hi()=0


James Kuyper

unread,
Aug 20, 2020, 4:23:49 PM8/20/20
to
On 8/20/20 3:09 PM, wyn...@gmail.com wrote:
...
> I am implementing a double-sized integer. This is the closest simplified version
> All the relevant members I can think of are shown in DxInt, g++ does not
When someone has a problem they don't understand, and posts only those
parts of the program that they think are relevant to the problem,
empirically it's very likely to be the case that the problem is in the
part they left out.

Start with the version of the program that fails, and then simplify it
one step at a time, testing after each removal to make sure that it
still fails. If removing a part makes it work, put that part back in.
Then stop and think very carefully about what you just did - you didn't
expect that removing that part would make a difference, but it did. That
can be a very useful clue toward figuring out what the real problem is.

When you can't find anything more that can be removed, and the program
still demonstrates the problem you're asking about, that's the time to
post the code here.

> complain others function members. Stll can't replicate the error.

You have still failed to post the program that actually fails. I can
give you some design advice about this program, but that's about it.

> test2.cpp may be correct. but i need time to verify this, meanwhile, may you
> people provides improvements of such implement, thanks.
>
> //-------------------------------------------------
> // test2.cpp
> #include <iostream>
>
> using namespace std;
>
> template<typename T> class DxInt;
>
> typedef uint32_t T;
> typedef uint64_t TT;
> template<> class DxInt<T> {
>
> T arr[2];
>
> public:
> DxInt() { *this=0; };
> DxInt(const DxInt& s) { *this=s; };
> DxInt(TT v) { *this=v; };
>
> inline const T& lo() const { return arr[0]; };
> inline T& lo() { return arr[0]; };
> inline const T& hi() const { return arr[1]; };
> inline T& hi() { return arr[1]; };

Keep in mind that, depending upon the target architecture, your lo()
function might return the high-order half of the integer. Middle-endian
machines are much rarer that either little-endian or big-endian
machines, but I've heard that there's still some in use - on such
machines, lo() will return something that is neither the hi nor the low
order half of the value.

I'd recommend re-naming these functions so that they portably describe
what they're actually doing: for instance, first_half() and
second_half(). If someone makes the mistake of thinking that
first_half() is guaranteed to contain the low-order bits, that will be
their mistake, not yours.

> inline DxInt& operator=(TT rhs) {
> //*this=rhs; // gdb halts at this line. seems a recusive call

It certainly is! I'm curious - why didn't you anticipate that it to be
recursive?

> // nothing to do with "cannot bind non-const lvalue
> // reference of type" or about "returning reference to
> // temporary"
> static_cast<TT&>(*this)=rhs;

The best way to do this would be to declare a union containing a
uint64_t object and an array of two uint32_t objects. Assign rhs to the
uint64_t object.

Jorgen Grahn

unread,
Aug 20, 2020, 5:30:53 PM8/20/20
to
On Thu, 2020-08-20, wyn...@gmail.com wrote:
...
> []$ g++ test.cpp

When you're fighting the compiler, it's better to ask it to produce as
detailed warnings as possible:

[]$ g++ -std=c++11 -Wall -Wextra -pedantic test.cpp

And optimize and support debugging:

[]$ g++ -std=c++11 -Wall -Wextra -pedantic -O2 -g test.cpp

There are many more warnings you can enable, but these are the ones I
normally use myself.

/Jorgen

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

Juha Nieminen

unread,
Aug 21, 2020, 6:39:14 AM8/21/20
to
wyn...@gmail.com wrote:
> class Arr {
> typedef uint16_t T;
>
> T arr[2];
>
> public:
> inline const T& lo() const { return arr[0]; };
> inline T& lo() { return arr[0]; };
> inline const T& hi() const { return arr[1]; };
> inline T& hi() { return arr[1]; };
> };

Those are not returning a reference to a temporary, so they don't have
such an issue.

wyn...@gmail.com

unread,
Aug 21, 2020, 7:03:44 AM8/21/20
to
Juha Nieminen於 2020年8月21日星期五 UTC+8下午6時39分14秒寫道:
Thanks Jorgen. Problem partially solved, compile time at least. I thought any
user copy assignment operator would disable the default one as the constructor
do! By now, running program compiled with -O2 showed something still not right


Jorgen Grahn

unread,
Aug 21, 2020, 7:28:27 AM8/21/20
to
On Fri, 2020-08-21, wyn...@gmail.com wrote:
> Juha Nieminen於 2020年8月21日星期五 UTC+8下午6時39分14秒寫道:
>> wyn...@gmail.com wrote:
...
>> Those are not returning a reference to a temporary, so they don't have
>> such an issue.
>
> Thanks Jorgen.

You mean Juha, but you are welcome anyway :-)

wyn...@gmail.com

unread,
Aug 21, 2020, 7:32:44 AM8/21/20
to
James Kuyper於 2020年8月21日星期五 UTC+8上午4時23分49秒寫道:
> On 8/20/20 3:09 PM, wyn...@gmail.com wrote:
> ...
> > I am implementing a double-sized integer. This is the closest simplified version
> > All the relevant members I can think of are shown in DxInt, g++ does not
> When someone has a problem they don't understand, and posts only those
> parts of the program that they think are relevant to the problem,
> empirically it's very likely to be the case that the problem is in the
> part they left out.
>
> Start with the version of the program that fails, and then simplify it
> one step at a time, testing after each removal to make sure that it
> still fails. If removing a part makes it work, put that part back in.
> Then stop and think very carefully about what you just did - you didn't
> expect that removing that part would make a difference, but it did. That
> can be a very useful clue toward figuring out what the real problem is.
>
> When you can't find anything more that can be removed, and the program
> still demonstrates the problem you're asking about, that's the time to
> post the code here.
>
> > complain others function members. Stll can't replicate the error.
>
> You have still failed to post the program that actually fails. I can
> give you some design advice about this program, but that's about it.
>

Thanks encouraging me to fallback to the very basic steps. I still skipped
too many. The crux: DxInt has several specializations for different types.
In the development, I modified DxInt data member (from two separate half
to one array) by text copy-paste or replacement, causing all the text form
of data members identical. But, forgot the data member of DxInt<uint8_t> is
different from other overloads, causing the bothering temporary object issue.
Programming is slowly becoming a mechanical thing to me, except dealing with
such kind of low-level problem.

James Kuyper

unread,
Aug 21, 2020, 9:10:04 AM8/21/20
to
On 8/21/20 7:32 AM, wyn...@gmail.com wrote:
> James Kuyper於 2020年8月21日星期五 UTC+8上午4時23分49秒寫道:
>> On 8/20/20 3:09 PM, wyn...@gmail.com wrote:
...
>> You have still failed to post the program that actually fails. I can
>> give you some design advice about this program, but that's about it.
>>
>
> Thanks encouraging me to fallback to the very basic steps. I still skipped
> too many. The crux: DxInt has several specializations for different types.
> In the development, I modified DxInt data member (from two separate half
> to one array) by text copy-paste or replacement, causing all the text form
> of data members identical. But, forgot the data member of DxInt<uint8_t> is
> different from other overloads, causing the bothering temporary object issue.

Does that mean that you've resolved the problem you were originally
asking about? If not, I recommend doing what you should have done from
the beginning: post code that actually demonstrates the problem you're
running into. Either the actual program that wouldn't compile, or
preferably a simplified version. But NOT a version that has been
simplified to the point that it no longer demonstrates the problem.

...
>>> inline DxInt& operator=(TT rhs) {
>>> //*this=rhs; // gdb halts at this line. seems a recusive call
>>
>> It certainly is! I'm curious - why didn't you anticipate that it to be
>> recursive?
>>
> Programming is slowly becoming a mechanical thing to me, except dealing with
> such kind of low-level problem.

I don't quite understand that answer - are you saying that programming
has become so mechanical to you that you've stopped thinking about what
the code you're writing actually means? If that results in you writing
code that is so obviously infinitely recursive without even being aware
of that fact, I recommend reversing that trend.

James Kuyper

unread,
Aug 21, 2020, 9:28:12 AM8/21/20
to
What does that have to do with your problem? The code above doesn't
involve any copy assignment operators. Other code that was in your
original message, but was clipped above, makes use of assignment
operators. However, it does so only for uint16_t, for which you cannot
provide a user-defined overload.

wyn...@gmail.com

unread,
Aug 21, 2020, 10:39:12 AM8/21/20
to
James Kuyper於 2020年8月21日星期五 UTC+8下午9時28分12秒寫道:
[]$ g++ test2.cpp -std=c++11 -Wall -Wextra -pedantic
test2.cpp: In copy constructor ‘DxInt<unsigned int>::DxInt(const DxInt<unsigned int>&)’:
test2.cpp:16:35: warning: implicitly-declared ‘DxInt<unsigned int>& DxInt<unsigned int>::operator=(const DxInt<unsigned int>&)’ is deprecated [-Wdeprecated-copy]
16 | DxInt(const DxInt& s) { *this=s; };
| ^
test2.cpp:16:5: note: because ‘DxInt<unsigned int>’ has user-provided ‘DxInt<unsigned int>::DxInt(const DxInt<unsigned int>&)’
16 | DxInt(const DxInt& s) { *this=s; };
| ^~~~~
test2.cpp: At global scope:
test2.cpp:53:2: warning: extra ‘;’ [-Wpedantic]
53 | };
| ^

//------------------------------------
Compiling with option -std=c++11 -Wall -Wextra -pedantic, showed the "implicitly-declared (operator=) is deprecated

I don't know why the default operator= can be be deprecated. However the
diagnosis message reminds me of lack of operator=(DxInt<T>&)

wyn...@gmail.com

unread,
Aug 21, 2020, 10:41:40 AM8/21/20
to
Jorgen Grahn於 2020年8月21日星期五 UTC+8下午7時28分27秒寫道:
Sorry, that's embarrassing, Juha

James Kuyper

unread,
Aug 22, 2020, 9:01:28 AM8/22/20
to
On 8/21/20 10:39 AM, wyn...@gmail.com wrote:
> James Kuyper於 2020年8月21日星期五 UTC+8下午9時28分12秒寫道:
>> On 8/21/20 7:03 AM, wyn...@gmail.com wrote:
>>> Juha Nieminen於 2020年8月21日星期五 UTC+8下午6時39分14秒寫道:
>>>> wyn...@gmail.com wrote:
>>>>> class Arr {
>>>>> typedef uint16_t T;
>>>>>
>>>>> T arr[2];
>>>>>
>>>>> public:
>>>>> inline const T& lo() const { return arr[0]; };
>>>>> inline T& lo() { return arr[0]; };
>>>>> inline const T& hi() const { return arr[1]; };
>>>>> inline T& hi() { return arr[1]; };
>>>>> };
>>>>
>>>> Those are not returning a reference to a temporary, so they don't have
>>>> such an issue.
>>>
>>> Thanks Jorgen. Problem partially solved, compile time at least. I thought any
>>> user copy assignment operator would disable the default one as the constructor

"If the class definition does not explicitly declare a copy assignment
operator, one is declared implicitly." 15.8.2p2.
You declared an assignment operator, but you did not declare a copy
assignment operator.

>>> do! By now, running program compiled with -O2 showed something still not right
>>
>> What does that have to do with your problem? The code above doesn't
>> involve any copy assignment operators. Other code that was in your
>> original message, but was clipped above, makes use of assignment
>> operators. However, it does so only for uint16_t, for which you cannot
>> provide a user-defined overload.
>
> []$ g++ test2.cpp -std=c++11 -Wall -Wextra -pedantic
> test2.cpp: In copy constructor ‘DxInt<unsigned int>::DxInt(const DxInt<unsigned int>&)’:
> test2.cpp:16:35: warning: implicitly-declared ‘DxInt<unsigned int>& DxInt<unsigned int>::operator=(const DxInt<unsigned int>&)’ is deprecated [-Wdeprecated-copy]
> 16 | DxInt(const DxInt& s) { *this=s; };
> | ^

So the code you're talking about is not the code that you quoted from
the message you responded to. Instead, it's code from a different branch
of this discussion. You should have posted this comment on that branch,
not here.

This warning is occurring inside the copy constructor, because you don't
have a user-declared copy assignment operator. -Wextra turns on
-Wdeprecated-copy, which will warn you if you provide a copy constructor
and rely upon an implicitly declared copy assignment operator, or if you
provide a user-declared copy assignment operator and rely upon an
implicitly-declared copy constructor. Note that this is only a warning.
You don't have to turn it on (I personally recommend against using
-Wextra), and you don't have to pay attention to it if you don't want to.

The warning is correct - the standard does deprecate such code:

"If the class definition does not explicitly declare a copy constructor,
a non-explicit one is declared implicitly.
If the class definition declares a move constructor or move assignment
operator, the implicitly declared copy constructor is defined as
deleted; otherwise, it is defined as defaulted (11.4). The latter case
is deprecated if the class has a user-declared copy assignment operator
..." (15.8.1p6)

"If the class definition does not explicitly declare a copy assignment
operator, one is declared implicitly.
If the class definition declares a move constructor or move assignment
operator, the implicitly declared copy assignment operator is defined as
deleted; otherwise, it is defined as defaulted (11.4). The latter case
is deprecated if the class has a user-declared copy constructor ..."
(15.8.2p2).

The idea behind those deprecations is that it is generally not
reasonable to use the implicitly-defined version of one of those
functions, while explicitly defining the other. Either the
implicitly-defined version of both functions should be acceptable, or
explicit definitions for both are needed.

Your copy constructor is implemented by calling the copy-assignment
operator which you have not explicitly declared, so one is implicitly
declared and implicitly defined, which is what triggers the warning.
That's unnecessary - an implicitly declared copy constructor would do
exactly the right thing with your class.

Note: reinterpret_cast<> is dangerous, and should be avoided if
possible. It's quite easy to avoid it in this case by using a union, and
the code gets significantly simpler if you use that approach.

wyn...@gmail.com

unread,
Aug 22, 2020, 3:35:03 PM8/22/20
to
James Kuyper於 2020年8月22日星期六 UTC+8下午9時01分28秒寫道:
I'd like to share your idea avoiding reinterpret_cast<> in the type
conversion operator and about implement of DxInt<T> (T is unsigned type)

James Kuyper

unread,
Aug 22, 2020, 4:57:04 PM8/22/20
to
On 8/22/20 3:34 PM, wyn...@gmail.com wrote:
> James Kuyper於 2020年8月22日星期六 UTC+8下午9時01分28秒寫道:
...
>> Note: reinterpret_cast<> is dangerous, and should be avoided if
>> possible. It's quite easy to avoid it in this case by using a union, and
>> the code gets significantly simpler if you use that approach.
>
> I'd like to share your idea avoiding reinterpret_cast<> in the type
> conversion operator and about implement of DxInt<T> (T is unsigned type)

I'm going to assume that "I'd like to share your idea ..." was actually
intended to be "I'd like you to share your idea ...". If so, here's your
class re-written along those lines:

#include <cstdint>
template <typename T> class DxInt;

typedef uint32_t T;
typedef uint64_t TT;

template <> class DxInt<T> {
union {
TT ui64;
T arr[2];
} u;

public:
DxInt() { u.ui64 = 0; };
DxInt(const DxInt& s) { u.ui64 = s.u.ui64; };
DxInt(TT v) { u.ui64 = v; };

inline const T& lo() const { return u.arr[0]; };
inline T& lo() { return u.arr[0]; };
inline const T& hi() const { return u.arr[1]; };
inline T& hi() { return u.arr[1]; };

inline DxInt& operator=(TT rhs) {
u.ui64 = rhs;
return *this;
};

operator TT& () { return u.ui64; };
operator const TT& () const { return u.ui64; };
};

Chris Vine

unread,
Aug 24, 2020, 4:03:06 PM8/24/20
to
This has been discussed many times before. For the information of the
OP, type punning with a union as in the lo() and hi() methods above has
undefined behaviour in C++, although it is supported by gcc and clang.
You can only legally access the currently active member of the union in
C++ (it is different in C).

Using reinterpret_cast is far worse (if that is what the OP has
been trying to do) because that is not supported by either gcc or clang
without the no-strict-aliasing flag. A standard conforming approach is
to use std::memcpy, which is also as efficient as a union in practice
because it is a compiler built-in.

Vir Campestris

unread,
Aug 24, 2020, 4:48:32 PM8/24/20
to
James, I admit I don't expect to find one anytime soon - but if your
compiler chooses to align uint32_t on 64-bit boundaries that's not going
to behave - specifically the copy constructor.

Or is there some corner of the spec I don't know that prohibits that?

Andy

James Kuyper

unread,
Aug 24, 2020, 5:35:29 PM8/24/20
to
In C90, "if a member of a union object is accessed after a value has
been stored in a different member of the object, the behavior is
implementation-defined.". It was pretty much universally the case that
implementations defined the behavior to be that "the appropriate part of
the object representation of the value is reinterpreted as an object
representation in the new type" (C2011 footnote 97). People wrote code
that depended on that fact.
This clause was dropped in C99. Many people claim that, as a result of
what the C99 standard said elsewhere about the representation of
objects, the behavior described above became mandatory. Personally, I
don't see how that conclusion can be derived from the words of the C99
standard.
In C2011, the C committee added the non-normative footnote 97 cited
above to confirm that this is the correct interpretation of the
normative words of the standard, without making any corresponding change
to the normative words. Under the circumstances, I'm willing to rely
upon footnote 97, despite not seeing how it's validity can be derived
from the normative words.

Given that history, I'm comfortable about making the same assumption in
C++ code, despite the fact that the wording of the C++ standard is still
closer to what C90 said than to what C2011 says.

> Using reinterpret_cast is far worse (if that is what the OP has
> been trying to do) because that is not supported by either gcc or clang
> without the no-strict-aliasing flag. ...

I wasn't recommending this approach in an absolute sense, only as an
improvement over the reinterpret_cast<> approach he was using.

> ... A standard conforming approach is
> to use std::memcpy, which is also as efficient as a union in practice
> because it is a compiler built-in.

Given
long l = 123456789;
int i;
std::memcpy(&i, &l, sizeof i);

does the C++ standard guarantee anything more about the value stored in
'i' than it does about the corresponding union code? If so, where?

James Kuyper

unread,
Aug 24, 2020, 5:56:52 PM8/24/20
to
No padding is allowed between consecutive elements in an array. This
implies that the size of a type includes all of the space between
consecutive elements of an array of that type, even if it doesn't
actually use all of that space. The exact-sized types such as uint32_t
are prohibited from having padding bits (that prohibition is in the C
standard, cross-referenced from the C++ standard). Therefore, a
conforming implementation of C++ is not allowed to use such a type for
uint32_t.

Chris Vine

unread,
Aug 24, 2020, 7:34:48 PM8/24/20
to
On Mon, 24 Aug 2020 17:35:18 -0400
[provisions of C standard concerning unions snipped]
> Given that history, I'm comfortable about making the same assumption in
> C++ code, despite the fact that the wording of the C++ standard is still
> closer to what C90 said than to what C2011 says.

The reputed effect of [class.union]/2 of C++ is that you can only read
the active member of a union, which is the member last written to, so
type punning through them is illegal. At least that is what Stroustrup
claims in TC++PL, as also does CppCoreGuidelines, and I have seen others
claim the same. The gcc developers treat union type punning as an
extension, not as a requirement arising from the standard. I do not
know whether or not Visual Studio allows type punning in C++ through
unions.

This has been gone into on this newsgroup before and I do not
particularly want to do so again. Are there contrary arguments?
Probably.

> > Using reinterpret_cast is far worse (if that is what the OP has
> > been trying to do) because that is not supported by either gcc or clang
> > without the no-strict-aliasing flag. ...
>
> I wasn't recommending this approach in an absolute sense, only as an
> improvement over the reinterpret_cast<> approach he was using.
>
> > ... A standard conforming approach is
> > to use std::memcpy, which is also as efficient as a union in practice
> > because it is a compiler built-in.
>
> Given
> long l = 123456789;
> int i;
> std::memcpy(&i, &l, sizeof i);
>
> does the C++ standard guarantee anything more about the value stored in
> 'i' than it does about the corresponding union code? If so, where?

The C++ standard incorporates relevant parts of the C standard library
into C++ in [library.c]/1 and [cstring.syn], as modified by
[diff.library]. That includes memcpy(), which is required by C to copy
the object representation of the value copied into the destination,
which is also more or less what in C++ [basic.types]/1 to /4 say.
Padding isn't an issue in this case, but for it to work, the object
representations of uint64_t and a 2-array of uint32_t must also in any
other respects correspond to each other.

In C, the same is also true of the use of unions for type punning. So
in C, the answer to your question is no. The issue in C++ is about the
supposed requirement relating to there being only one active type.

On the issue of memcpy() versus a cast, the point is that with a cast
the effective type (C) and dynamic type (C++) of the result of the cast
remains that of the source. With memcpy() it is that of the
destination. So memcpy() does not give rise to strict aliasing issues,
whereas a cast does. Using reinterpret_cast in this case clearly
generates undefined behaviour.

Chris Vine

unread,
Aug 24, 2020, 7:41:56 PM8/24/20
to
On Tue, 25 Aug 2020 00:34:36 +0100
Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote:
[snip]
> The C++ standard incorporates relevant parts of the C standard library
> into C++ in [library.c]/1 and [cstring.syn], as modified by
> [diff.library]. That includes memcpy(), which is required by C to copy
> the object representation of the value copied into the destination,
> which is also more or less what in C++ [basic.types]/1 to /4 say.

By the way, one interesting factlet is that you cannot implement
std::memcpy() using standard C++ conforming to C++17/20, because of
restrictions on pointer arithmetic in those standards. Its
implementation is therefore compiler-primitive specific, mandated by
the provisions I have referred to.

Vir Campestris

unread,
Aug 27, 2020, 5:14:41 PM8/27/20
to
On 24/08/2020 22:56, James Kuyper wrote:
> No padding is allowed between consecutive elements in an array.

OK, thanks. And those old mainframes with funny word sizes (I've used 24
and 36 bits) are dead and not mourned :)

Andy
--
FWIW ICL 1900 and DECSystem10. And neither of them with C or C++!
0 new messages