1

Is it safe to use memmove/memcpy to initialize an object with constructor parameters?
No-one seems to use this method but it works fine when I tried it.
Does parameters being passed in a stack cause problems?

Say I have a class foo as follows,

class foo
{
  int x,y;
  float z;
  foo();
  foo(int,int,float); 
};

Can I initialize the variables using memmove as follows?

foo::foo(int x,int y,float z)
{
  memmove(this,&x, sizeof(foo));
}
  • 4
    Why on earth are you doing this? Makes assumptions. and have code unreadable. Portability is out of the window – Ed Heal Sep 24 '16 at 14:38
  • I'd say it's not. Compiler is free to pass arguments in another order (abi). And anyway, being scalars, the parameters are probably already in registers, and should stay there – Regis Portalez Sep 24 '16 at 14:38
  • @RegisPortalez the compiler won't use a register if the address of the variable is taken – M.M Sep 24 '16 at 14:41
  • @EdHeal Just to avoid writing `this->x = x` repeatedly –  Sep 24 '16 at 14:46
  • @M.M: Since the code only takes the address of `x`, the other parameters could well be passed in registers. All the more reason not to do this (if *undefined behavior* wasn't convincing enough already). – IInspectable Sep 24 '16 at 14:58
  • @SrinagRao [Your class fails the std::is_pod test](http://coliru.stacked-crooked.com/a/9155d4482f68b9c6). The output of that program is 0. Using functions such as `memmove`, `memcpy`, etc, on non-POD types is undefined behavior. – PaulMcKenzie Sep 24 '16 at 15:23

4 Answers4

3

This is undefined behavior.

The shown code does not attempt to initialize class variables. It attempts to memmove() onto the class pointer, and assumes that the size of the class is 2*sizeof(int)+sizeof(float). The C++ standard does not guarantee that.

Furthermore, the shown code also assumes the layout of the parameters that are passed to the constructor will be the same layout as the layout of the members of this POD. That, again, is not specified by the C++ standard.

It is safe to use memmove to initialize individual class members. For example, the following is safe:

foo::foo(int x_,int y_,float z_)
{
   memmove(&x, &x_, sizeof(x));
   memmove(&y, &y_, sizeof(y));
   memmove(&z, &z_, sizeof(z));
}

Of course, this does nothing useful, but this would be safe.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
  • It doesn't matter which word you prefer to use. The answer is the same. – Sam Varshavchik Sep 24 '16 at 14:49
  • Got it. Didn't know the layout needn't be the same. –  Sep 24 '16 at 14:50
  • @SrinagRao: Add a virtual destructor to your class and calculate the [offsetof](http://en.cppreference.com/w/cpp/types/offsetof) your class members. `x` may or may not be at offset 0. With virtual class members it's likely not. – IInspectable Sep 24 '16 at 14:55
1

No it is not safe, because based on the standard the members are not guaranteed to be immediately right after each other due to alignment/padding. After your update, this is even worse because the location of passed arguments and their order are not safe to use.

masoud
  • 55,379
  • 16
  • 141
  • 208
1

We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%. - Donald Knuth

You should not try to optimize a code you are not sure you need to. I would suggest you to profile your code before you are able to perform this kind of optimizations. This way you don't lose time improving the performance of some code that is not going to impact the overall performance of your application.

Usually, compilers are smart enough to guess what are you trying to do with your code, and generate high efficient code that will keep the same functionality. For that purpose, you should be sure that you are enabling compiler optimizations (-Olevel flag or toggling individual ones through compiler command arguments).

For example, I've seen that some compilers transform std::copy into a memcpy when the compiler is sure that doing so is straightforward (e.g. data is contiguous).

Jorge Bellon
  • 2,901
  • 15
  • 25
0

No it is not safe. It is undefined behavior.

And the code

foo::foo(int x,int y,float z)
{
  memmove(this,&x, sizeof(foo));
}

is not even saving you any typing compared to using an initializer list

foo::foo(int x,int y,float z) : x(x), y(y), z(z)
{ }
Bo Persson
  • 90,663
  • 31
  • 146
  • 203