How to Perform Deep Copies of YAML Nodes in yaml-cpp
Use YAML::Clone(node) to create a deep copy of a YAML node in yaml-cpp, as the default copy constructor and assignment operator only perform shallow copies that share underlying memory.
In the jbeder/yaml-cpp library, YAML::Node objects manage their underlying data through a shared-memory holder. While this design makes shallow copies efficient, performing deep copies of YAML nodes in yaml-cpp requires explicit use of the YAML::Clone function rather than the standard copy constructor.
Understanding Shallow vs Deep Copy Semantics
The YAML::Node class defined in include/yaml-cpp/node/node.h provides a copy constructor (Node(const Node& rhs)) and assignment operator that perform shallow copies. These operations copy the smart pointer to the internal node data, meaning both the original and the copy reference the same memory.
When you modify a shallow copy, you simultaneously modify the original node because they share the same underlying node_data structure. This behavior is efficient for passing nodes around but problematic when you need independent copies.
How to Deep Copy a YAML Node Using YAML::Clone
To create an independent deep copy, use the YAML::Clone function declared in include/yaml-cpp/node/node.h and implemented in src/node.cpp. This function constructs a completely new node tree by emitting the source node's events into a fresh NodeBuilder.
The implementation in src/node.cpp follows this pattern:
Node Clone(const Node& node) {
NodeEvents events(node);
NodeBuilder builder;
events.Emit(builder);
return builder.Root();
}
Here, NodeEvents (from src/nodeevents.h) captures the source node's structure and values, while NodeBuilder (from src/nodebuilder.h) constructs a new node hierarchy with its own memory allocation.
The Clone Implementation Details
The deep copy process works by:
- Creating a
NodeEventsobject that traverses the source node and records its structure - Instantiating a
NodeBuilderto construct a fresh node tree - Emitting the recorded events into the builder, which reconstructs the entire hierarchy
- Returning the root node from the builder, which now owns its own memory allocation
This process ensures that scalars, sequences, and maps are all duplicated with independent storage.
Practical Code Examples
The following example demonstrates the difference between shallow and deep copying:
#include <yaml-cpp/yaml.h>
#include <iostream>
int main() {
// Load a YAML document with nested structure
YAML::Node original = YAML::Load(R"(
user:
name: Alice
age: 30
hobbies:
- reading
- hiking
)");
// Shallow copy: shares underlying memory
YAML::Node shallow = original;
shallow["user"]["age"] = 31; // Modifies original as well
std::cout << "Original age: " << original["user"]["age"].as<int>() << "\n"; // Output: 31
// Deep copy: independent memory using YAML::Clone
YAML::Node deep = YAML::Clone(original);
deep["user"]["age"] = 32; // Does NOT affect original
std::cout << "Original age after deep copy mod: " << original["user"]["age"].as<int>() << "\n"; // Output: 31
std::cout << "Deep copy age: " << deep["user"]["age"].as<int>() << "\n"; // Output: 32
}
When to Use Deep Copies vs Shallow Copies
Use shallow copies when you want to pass nodes by value without the overhead of duplicating data, or when multiple parts of your code need to view and modify the same underlying configuration.
Use deep copies via YAML::Clone when you need to:
- Modify a node without affecting the original template
- Branch processing logic where each branch needs independent state
- Preserve the original document while building a modified variant
Summary
- The default
YAML::Nodecopy constructor ininclude/yaml-cpp/node/node.hperforms a shallow copy, sharing memory with the source node. - Deep copies require calling
YAML::Clone(node), which creates an independent node tree usingNodeEventsandNodeBuilderas implemented insrc/node.cpp. - Shallow copies reflect modifications across all references, while deep copies produced by
Cloneare completely isolated. - Use
YAML::Clonewhenever you need to mutate a node hierarchy without affecting the original document.
Frequently Asked Questions
Does yaml-cpp's copy constructor create a deep copy?
No, the copy constructor Node(const Node& rhs) defined in include/yaml-cpp/node/node.h creates a shallow copy that shares the underlying memory holder. Modifications to the copy will affect the original node. Use YAML::Clone for deep copying.
What is the difference between YAML::Clone and the copy constructor?
The copy constructor performs a shallow copy by copying the shared pointer to internal data, while YAML::Clone creates a completely new node tree by replaying the source node's events through a NodeBuilder. As implemented in src/node.cpp, Clone ensures the new node owns its own memory allocation.
How does YAML::Clone handle complex nested nodes?
YAML::Clone recursively processes the entire node hierarchy using NodeEvents to emit events for all nested maps, sequences, and scalars, then reconstructs them via NodeBuilder. This creates a full deep copy of the entire tree structure, not just the top-level node.
Is YAML::Clone efficient for large documents?
While YAML::Clone is more expensive than shallow copying due to the event emission and reconstruction process, it is the only way to achieve true data independence. For very large documents where performance is critical, consider using shallow copies with immutable access patterns or restructuring your logic to avoid deep copies when possible.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →