C++ tidbit #6: Virtual functions and trivial copy

C++ tidbit #6: Virtual functions and trivial copy

Trivial Copyability

In C++ terms an object is `trivially-copyable` if it is ok to memcpy it around. Containers (stl and custom) typically rely on `std::is_trivially_copyable<T>` to specialize copy operations of a bulk of T objects. Techincally, A copy ctor is considered trivial when :

  1. It is not user-provided,
  2. The underlying class has no virtual functions,
  3. <other conditions>

Condition (1) seems reasonable, but why condition (2)? What might go wrong when memcpy'ing an instance of a class with virtual functions?

Object Slicing

Let's focus on this toy snippet.


struct B {
? ? int b; 
? ? virtual void f(); 
};
struct D : B { 
? ? int d; 
? ? void f() override;
};        

Everything is fine if you memcpy real B's around, and everything is fine if you memcpy real D's.

But what about -


  D d;
  B& b = d;    // can we memcpy?        

This is essentially object slicing: assigning a derived object to a parent (and in the process, 'slicing' away the derived-only members). Our code is more referencing than assigning, but the essential problem remains if we try to memcpy:

No alt text provided for this image

The 1st element of the object's memory is a pointer to the class' vtable (a table of pointers to the virtual functions, shared among all class instances). A few things to note:

  1. When assigning to the reference-to-base `B& b = d` the vfptr remains unchanged, and so b.f() would invoke D::f, as desired.
  2. The d member is inaccessible via the B reference, but it is in place for use by D::f as needed.
  3. Any reasonable routine memcpy'ing a B object would memcpy only sizeof(B) bytes, thereby creating an object in an inconsistent state: calling f would invoked the derived D::f which could try to use the derived member D::b, but nothing meaningful is there.

要查看或添加评论,请登录

Ofek Shilon的更多文章

  • Compiler Limitations #3/3

    Compiler Limitations #3/3

    Some examples, before the main point As discussed in the 1st post in this series, clang isn't able to properly express…

    3 条评论
  • C++ tidbit #10: `=default` Impact on Initialization

    C++ tidbit #10: `=default` Impact on Initialization

    Here's just one gotcha of dozens lurking within C++ initializations. The following is called 'value initialization':…

    3 条评论
  • C++ tidbit #9: nullptr_t doesn't behave like a pointer type?

    C++ tidbit #9: nullptr_t doesn't behave like a pointer type?

    Suppose you want to customize some behavior for compile-time nullptrs. As a concrete example let's take a comparison of…

    11 条评论
  • C++ tidbit #8: Damaging Default Destructor

    C++ tidbit #8: Damaging Default Destructor

    Special member functions are implicitly generated by the compiler if the user didn't provide them - constructors…

    9 条评论
  • C++ tidbit #7: Lifetime Extension by Const Ref

    C++ tidbit #7: Lifetime Extension by Const Ref

    Const-ref can bind to a temporary..

    11 条评论
  • C++ tidbit #5: const static members

    C++ tidbit #5: const static members

    struct S { static const int x = 0; }; int? n = S::x; const int m = S::x; -- link error: undefined reference to…

    2 条评论
  • C++ tidbit #4: Signed UB

    C++ tidbit #4: Signed UB

    All happy integers are alike, but every unhappy integer is unhappy in its own way Since the early computing days all…

    7 条评论
  • C++ tidbit #3: Contextual Conversion

    C++ tidbit #3: Contextual Conversion

    Warmup: explicit constructors When you mark a cast operator as explicit: struct C { explicit operator bool() {…

    1 条评论

社区洞察

其他会员也浏览了