12 Advanced Puzzles, Tuples & Operators

Overview: Advanced Edge Cases & Type Grouping

This note covers advanced C++ edge cases, including Object Slicing, overriding private virtual methods, std::tuple operations, and logical keyword alternatives.


1. Object Slicing Mechanics

Object Slicing occurs when you assign a derived class object to a base class object by value.

Derived derivedObj;
Base baseObj = derivedObj; // Slicing occurs here!

Under the Hood:

  1. Memory Allocation: The stack frame allocates only enough space for the size of Base.
  2. Member Discarding: During assignment, the copy constructor of Base runs. It copies the base portion of derivedObj into baseObj and discards (slices off) all member variables unique to Derived.
  3. Vptr Reset: The _vptr of baseObj is set to point to Base’s vtable, meaning it loses all polymorphic behavior.

How to prevent Slicing:

Always pass objects polymorphically by Reference (Base&) or Pointer (Base*). This maintains the original object memory layout and preserves the _vptr pointing to the derived class’s vtable.


2. Overriding Private Virtual Functions

An unusual but valid feature of C++: You can override private virtual functions of a base class.

class Base {
    virtual void doWork(); // Private
};
 
class Derived : public Base {
    void doWork() override; // Overrides Base::doWork
};

Why does this compile?

  • Access Control (public/private) is verified by the compiler at compile time.
  • Virtual Method Lookup (Dynamic Dispatch) occurs at runtime using the vtable.
  • The derived class cannot directly call the base private function, but it can override it to provide custom behavior when triggered from within the base class.

3. Strong Tuples (std::tuple)

std::tuple generalizes std::pair to hold an arbitrary number of values of different types.

auto t = std::make_tuple(10, 'A', 3.14);
int val = std::get<0>(t); // Retrieves first element

Unpacking via std::tie:

You can unpack tuples directly into individual variables:

int x; char y; double z;
std::tie(x, y, z) = t; // Unpacks elements into x, y, and z

Use std::ignore as a placeholder if you want to skip unpacking specific elements.


4. Alternative Logical & Bitwise Keywords

C++ supports text-based alternative representations for operators (traditionally defined in <ciso646> but supported natively by modern compilers):

  • and &&
  • or ||
  • not !
  • xor ^
  • bitand &
  • bitor |
  • compl ~

πŸ’» Conceptual Code Demonstration

#include <iostream>
#include <tuple>
 
class Parent {
public:
    virtual void print() const { std::cout << "Parent class\n"; }
};
 
class Child : public Parent {
public:
    void print() const override { std::cout << "Child class\n"; }
};
 
// Function demonstrating slicing vs reference parameter
void triggerSlicing(Parent p) { p.print(); }       // Slices object!
void triggerRef(const Parent& p) { p.print(); }    // Preserves polymorphism
 
int main() {
    Child c;
    
    std::cout << "--- 1. Object Slicing Demonstration ---\n";
    triggerSlicing(c); // Prints: Parent class
    triggerRef(c);     // Prints: Child class
    
    std::cout << "\n--- 2. Tuples Packing & Unpacking ---\n";
    auto data = std::make_tuple(1, "Alice", 98.6);
    
    int id;
    std::string name;
    double score;
    
    // Unpack data tuple
    std::tie(id, name, score) = data;
    std::cout << "Unpacked: " << name << " (ID: " << id << ") -> " << score << '\n';
    
    // 3. Alternative operator keywords
    if (true and not false) { // Equivalent to: true && !false
        std::cout << "Logical keywords compiled successfully!\n";
    }
    
    return 0;
}