1

Edit: this seems to address perfectly my question.


Regarding Which type trait would indicate that type is memcpy assignable? (tuple, pair)

The answer is that std::is_trivially_copyable would be able to tell me if I can safely memcpy around my object.

Now consider:

#include <iostream>
#include <type_traits>

struct A {
    int x;
    int y;
    A(const A& other) : x(other.x), y(other.y){}
};

struct B {
    int x;
    int y;
    B(const B& other) = default;
};

int main() 
{
    std::cout << std::boolalpha;
    std::cout << std::is_trivially_copyable<A>::value << '\n'; //false
    std::cout << std::is_trivially_copyable<B>::value << '\n'; //true
}

A and B are to all practical effect the same, however A is detected as not trivially copyable.

So, which risk am I would be taking in memcpy-ing such an object?

The use case: I would like to have a dynamic array (not vector, and which I would make grow with realloc) of cv::Point in opencv library, which probably because of C++98 compatibility doesn't use default to define the copy constructor.

Antonio
  • 19,451
  • 13
  • 99
  • 197
  • If the type's copy constructor really does just copy the bytes, the compiler will know to optimize it to `memcpy` anyway. Don't sweat it. – Brian Bi Mar 13 '18 at 23:45
  • @Brian See edit: my precise use case is an array of `cv::Point` where I would use a realloc (I understand the check on `std::is_trivially_destructible` becomes superfluous when `std::is_trivially_copyable` is passed) – Antonio Mar 13 '18 at 23:48
  • Why would you memcpy the objects when that is what the default copy constructor will do? –  Mar 14 '18 at 00:05
  • @NeilButterworth If I want to copy one array of `cv::Point` into another (assume I have good reasons not to use a `std::vector`) – Antonio Mar 14 '18 at 00:16
  • @Antonio: I think Neil's point is that `std::copy` will implicitly invoke the default copy constructor for you, so why not just do that instead of testing the limits of defined behavior? Is `std::copy(&src[0], &src[len], &dst[0])` or `std::copy_n(&src[0], len, &dst[0])` somehow worse than `memcpy(&dst[0], &src[0], len * sizeof(src[0]))`? – ShadowRanger Mar 14 '18 at 00:32
  • @ShadowRanger Good point, but again this doesn't cover `realloc`! – Antonio Mar 14 '18 at 00:36
  • Was about to write the exact same thing as the dupe. Really *really* thorough explanation there, so I stopped – Passer By Mar 14 '18 at 02:13
  • Can't you use allocate the array with `new`? – HolyBlackCat Mar 14 '18 at 11:23
  • `realloc` will copy the memory for you if it moves the allocation(!) – Will Crawford Mar 18 '18 at 15:46
  • @WillCrawford And so? The point is that if the object is not trivially copiable, then we are invoking undefined behaviour. See [here](https://stackoverflow.com/questions/29777492/why-would-the-behavior-of-stdmemcpy-be-undefined-for-objects-that-are-not-triv/29777728#29777728). – Antonio Mar 19 '18 at 20:44
  • Point being, we'd need a `renew` operator, I guess :) leaving it to manual handling is error-prone as well as tending to cause "undefined" behaviour – Will Crawford Mar 19 '18 at 21:40

3 Answers3

0

The chief risk is that the optimizer outright removes the call to memcpy. That's a perfectly reasonable optimization: the fastest code is code which isn't called, and it's safe to assume code isn't called with incorrect parameters.

This also leads to the second risk: the optimizer may remove not just the memcpy call, but all code inevitably leading up to it. It may backtrack to a preceding if statement, observe which branch leads to the memcpy, and just remove that whole branch and the test condition too.

MSalters
  • 173,980
  • 10
  • 155
  • 350
0

The compiler sees you have a copy constructor and backs off. It doesn't care what is in it, though it would be good if this simple case where the assignments add nothing to the default was detected by the compiler.

In this particular case, you can get away with it, no real harm done.
On the other hand, you leave a maintenance nightmare for the next developer.
Can't you get rid of the constructor, or replace it with =default?

Also, I'm not sure memcpy would be significantly faster than a loop-unrolled and inlined chain of these simple constructor calls?

Gem Taylor
  • 5,381
  • 1
  • 9
  • 27
-2

You can memcpy the objects back and forth as much as you want. The binary representation and alignment requirements of the structs are the same.

If you really don't believe it, you can create static_asserts that check that the size and offsets are the same.

wayfarer
  • 11
  • 6