Reinitialize Structure Object

K

Keith H Duggar

* Helge Kruse, on 21.06.2010 16:15:


No, there is no copy constructor call involved.

It has /the same effect/ as creating a temporary TestStructure,
value-initializing that structure, and calling the copy assignment operator.

For a POD value-initialization reduces to zero-initialization.

Whether an actual assignment (copying) is performed is a quality of
implementation issue; a good compiler may just call memset, but with the
difference that the source code is now safe.

Whether stack space is used is a quality of implementation issue.

Two other alernatives are

swap(myTest,TestStructure()) ;
myTest = move(TestStructure()) ; //C++0x

where the "swap trick" can be very useful for containers etc.
That's both inefficient and needlessly unconventional <g>.

A constructor deals with raw, uninitialized memory, e.g. nothing needs to be
deallocated. Assignment deals with initialized stuff, where e.g. something may
need to be deallocated. An Initialize method that could be called later would
likewise have to deal with and have as basic assumption, initialized stuff.

And that means (1) inefficiency of construction, because the constructor would
first have to default initialize and then call Initialize, and (2) that
Initialize plays the role of a special-cased copy assignment operator.

So why not use the copy assignment operator for assignment (that's what it's /for/)?

And for construction you really don't want to get into the habit of expressing
construction in terms of assignment, if you can help it. It yields extreme
redundance, subtleties of multiple initializations where bugs will creep in (not
to mention inefficiency), and is generally not exception safe. The conventional
way is instead to express assignment in terms of construction.

+1

Please consider taking Alf's advice above. C++ has constructors,
destructors, copy, swap, and move idioms. With that full toolset
there is almost never a need for "initialize" methods. Strongly
prefer some combination of those together with RAII/RRID rather
than whatever "initialize" pattern.

KHD
 
K

Keith H Duggar

[..memset or assignment from a temporary..]
Two other alernatives are
swap(myTest,TestStructure()) ;
myTest = move(TestStructure()) ; //C++0x
where the "swap trick" can be very useful for containers etc.

Except that swap requires lvalues, and a temporary created as the second
argument to 'swap' *isn't*.

Unless you have rvalue reference support turned on ;-) If not
then yeah we have to use the more intrusive (but original) form
of the "swap trick"

TestStructure().swap(myTest) ;

KHD
 
H

Helge Kruse

Alf P. Steinbach /Usenet said:
No, there is no copy constructor call involved.
Your're right. It's assignement and not copy-construction.
Whether an actual assignment (copying) is performed is a quality of
implementation issue; a good compiler may just call memset, but with the
difference that the source code is now safe.
is now safe instead of ... ?
Whether stack space is used is a quality of implementation issue.
When you create a temporary TestStructure, you need to allocate memory for
it.
If the type does not support copying then a memset will probably be doing
The Wrong Thing.

Do you expect that

struct TestStructure
{
int aa, bb, cc, dd;
} myTest = { 0 };

and

struct TestStructure myTest;
memset(&myTest, 0, sizeof(myTest));

give different result. The OP asked for a reset to 0-values of a POD as in
the definition with initialization above, didn't he? For the actual
question, the memset is still setting all members to zero, as you wrote
a good compiler may just call memset


Helge
 
J

Jorgen Grahn

* Mike Copeland, on 19.06.2010 20:18:

[Visual Studio 6]
Being able to use modern libraries such as Boost is pretty compelling... ;-)

Does even the /standard/ library even work properly with VS6?

To elaborate a bit: I understand that VS6 looks normal to Mike, but
it's looking more and more odd to others. Working with it (and finding
other people to work with it) will become harder and harder, and
finally impossible. So it's time to start looking at how to migrate.

(I have little experience with VS6, but I know how painful it is for
me to use gcc 2.95 today. That compiler was what I used all day long
and trusted in 1999.)

/Jorgen
 
A

Alf P. Steinbach /Usenet

* Helge Kruse, on 22.06.2010 08:44:
Your're right. It's assignement and not copy-construction.


is now safe instead of ... ?

Strict typechecking as with "=" yields a measure of safety.

Doing away with typechecking, like using memset, yields some unsafety.

It is of course (as I see it) a personal preference whether you want to spend
your own and possibly your employer's time on tracking down and fixing erroneous
uses of memset just to gain a possible micro-optimization. So if you think
memset is your thing, well then it is your thing. I'm just saying that *if* the
goal is correctness and avoiding that work, then prefer other means.

For example, I've mentioned the x = T() technique, which is type safe.

Another technique is to use std::fill for an array, which with a good compiler
and library implementation will translate to a memset /when that is correct/.

When you create a temporary TestStructure, you need to allocate memory for
it.

Formally the compiler doesn't need to do that. It's only bound by the "as if"
rule. A sufficiently good compiler will optimize the whole shebang away when
it's asked to optimize. But on the downside, for the in-practice, neither g++
nor MSVC seem to do this no-temporary optimization, at least in Windows.

E.g., MSVC does recognize that it can emit code equivalent to memset, but it
does that by (effectively) memset'ing a temporary and then memmove'ing it over.

But I wouldn't worry about that; rather, if the stack space is a problem, I
would worry about the size of the structure, which then is a code smell.

Do you expect that

struct TestStructure
{
int aa, bb, cc, dd;
} myTest = { 0 };

and

struct TestStructure myTest;
memset(&myTest, 0, sizeof(myTest));

give different result.

Uhm, AFAICS the question has no relationship to what you quoted.

But to answer the question, if you put in a floating point member, then,
although it will work in practice, formally you're on shaky ground. With IEEE
754 you have all bits 0 equals 0.0 value, but with some other apparently now no
longer used floating point representations it is not so. I think it's telling
that while the formal lack of a guarantee is mentioned just about every time
memset is discussed, it seems that no one is able to give a reference to an
actual, extant floating representation where all bits 0 is not 0.0.

And ditto for pointer values: no formal guarantee, but no example known (to me,
at least) where it won't work. :)

So regarding whether it will work in practice when done correctly, yes, as far
as I know.

That said, just try googling for memset problems, if you haven't personally
spent some hours tracking down such problems.

The OP asked for a reset to 0-values of a POD as in
the definition with initialization above, didn't he? For the actual
question, the memset is still setting all members to zero, as you wrote

No, there is a huge difference between compiler-generated code calling memset
(or emitting equivalent machine code, as MSVC does), and you calling memset
directly in the source code. The former is safe, and it will change as necessary
with the evolution of the code. The latter is brittle and unsafe in itself, and
will not change automatically to reflect changes of the code.

It's on a par with inline assembly code.

Assembly generated by the compiler is safe, and it changes with the source
code's evolution; inline assembly specified by you is not, and does not.


Cheers & hth.,

- Alf
 
P

Paul Bibbings

Leigh Johnston said:
I am a bit of a newbie as far as rvalue references are concerned so I
am not sure I understand how they help: isn't std::swap defined to be
std::swap(T&, T&) and not std::swap(T&&, T&&)? Please enlighten me.
I cannot get std::swap to work with rvalue references in g++ v4.4.1
(c++0x extensions enabled).

16:14:05 Paul Bibbings@JIJOU
/cygdrive/d/CPPProjects/CLCPP $cat swap_ex.cpp
// file: swap_ex.cpp

#include <utility>
#include <iostream>

template<typename T>
class A {
public:
A() { std::cout << "ctor\n"; }
A(const A& a) { std::cout << "copy\n"; }
A(A&& a) { std::cout << "move\n"; }
};

int main()
{
A<int>&& a1 = A<int>();
A<int>&& a2 = A<int>();
std::swap(a1, a2);
}

16:14:11 Paul Bibbings@JIJOU
/cygdrive/d/CPPProjects/CLCPP $i686-pc-cygwin-g++-4.4.1 -std=c++0x -o
swap_ex swap_ex.cpp

16:16:43 Paul Bibbings@JIJOU
/cygdrive/d/CPPProjects/CLCPP $./swap_ex
ctor
ctor
move

Regards

Paul Bibbings
 
K

Keith H Duggar

Yes but Keith Duggar was not using named rvalue references as in your
example, he was using an lvalue and a temporary as parameters to std::swap
which should never work.

No, Keith Duggar wrote "swap" and Leigh imagined it had std:: at
the front. I write overloads of swap for UDTs that implement the
improved swap interface just as descibed in the rvalue proposal

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1690.html#Improved swap Interface

and also exeplified in N3092 20.2.2 as the "value_swap" function.

KHD
 
K

Keith H Duggar

On 6/21/2010 12:57 PM, Keith H Duggar wrote:
[..memset or assignment from a temporary..]
Two other alernatives are
swap(myTest,TestStructure()) ;
myTest = move(TestStructure()) ; //C++0x
where the "swap trick" can be very useful for containers etc.
Except that swap requires lvalues, and a temporary created as the second
argument to 'swap' *isn't*.
Unless you have rvalue reference support turned on ;-) If not
then yeah we have to use the more intrusive (but original) form
of the "swap trick"
TestStructure().swap(myTest) ;

His POD has no member functions.

Some context has been snipped. I am responding in general terms and
most ardently to the following idea

I would prefer an Initialize() method that is called by the constructor and
can be called else where if needed.

of using "Initialize" methods. I'm saying that C++ has a rich set
of superior alternatives in the form of construct, assign, move,
copy, swap that should be highly preferred to any "Initialize"
pattern for the purpose of "resetting" an object.

So if one is even thinking of adding an (Re)Initialize() function
to one's structure, I am saying one should instead consider adding
a swap overload or if you do not have (or don't want to use) rvalue
support then a swap member.

KHD
 
P

Paul Bibbings

Leigh Johnston said:
Yes but Keith Duggar was not using named rvalue references as in your
example, he was using an lvalue and a temporary as parameters to
std::swap which should never work. Also aren't named rvalue
references treated as lvalues?

Okay. That is indeed different and it wouldn't work with the
temporary. Also you are correct about named rvalue references behaving
like lvalues under certain circumstances, such as the above.

As to your earlier question, then, I think that the implementation of
std::swap for gcc would be something like:

template<typename T>
struct remove_reference
{ typedef T type; };

template<typename T>
struct remove_reference<T&>
{ typedef T type; };

template<typename T>
struct remove_reference<T&&>
{ typedef T type; };

template<typename T>
inline typename remove_reference<T>::type&&
move(T&& a)
{
typedef typename remove_reference<T>::type&& RvalRef;
return static_cast<RvalRef>(a);
}

template<typename T>
inline void swap(T& a, T& b)
{
T tmp = move(a);
a = move(b);
b = move(tmp);
}

For this defintion a temporary is not going to bind to either
parameter to swap. In the example that I gave, IIUC, it is just for the
reason that you gave - that a named rvalue reference is treated
thereafter like an lvalue - that the call was successful.

However, you can use a little indirection to get a temporary to work as
in Keith's example, with something like:

template<typename T>
inline void my_swap(T&& a, T&& b)
{
swap(a, b);
}

int main()
{
A<int> a;
my_swap(move(a), A<int>());
}

Regards

Paul Bibbings
 
K

Keith H Duggar

Sorry, forgot about your overloading habits. One wonders why instead of the
"value_swap" example the following was not added to the standard instead:

template <typename T, typename U>
void swap(T&& left, U&& right)
{
if (&left != &right)
{
typename remove_reference<T>::type tmp = move(left);
left = move(right);
right = move(tmp);
}
}

I am probably missing something obvious as to why.

According to Howard Hinnant (with David Abrahams also involved
in the discussion) this was the killer argument

http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-closed.html#884

KHD
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,773
Messages
2,569,594
Members
45,123
Latest member
Layne6498
Top